mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
* [AC-431] Add new organization invite process (#4841) * [AC-431] Added properties 'key' and 'keys' to OrganizationUserAcceptRequest * [AC-431] On organization accept added check for 'initOrganization' flag and send encrypt keys if true * [AC-431] Reverted changes on AcceptOrganizationComponent and OrganizationUserAcceptRequest * [AC-431] Created OrganizationUserAcceptInitRequest * [AC-431] Added method postOrganizationUserAcceptInit to OrganizationUserService * [AC-431] Created AcceptInitOrganizationComponent and added routing config. Added 'inviteInitAcceptedDesc' to messages * [AC-431] Remove blank line * [AC-431] Remove requirement for logging in again * [AC-431] Removed accept-init-organization.component.html * Update libs/common/src/abstractions/organization-user/organization-user.service.ts Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [AC-431] Sending collection name when initializing an org * [AC-431] Deleted component accept-init-organization and incorporated logic into accept-organization * Update libs/common/src/abstractions/organization-user/organization-user.service.ts Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [AC-431] Returning promise chains * [AC-431] Moved ReAuth check to org accept only * [AC-431] Fixed import issues --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [AC-434] Hide billing screen for reseller clients (#4955) * [AC-434] Retrieving ProviderType for each Org * [AC-434] Hide subscription details if user cannot manage billing * [AC-434] Renamed providerType to provider-type * [AC-434] Reverted change that showed Billing History and Payment Methods tabs * [AC-434] Hiding Secrets Manager enroll * [AC-434] Renamed Billing access variables to be more readable * Apply suggestions from code review Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [AC-434] Reduce duplication in permission code * [AC-434] npm prettier * [AC-434] Changed selfhost subscription permission * [AC-434] Added canEditSubscription check for change plan buttons * [AC-434] Removed message displaying provider name in subscription * [AC-434] canEditSubscription logic depends on canViewSubscription * [AC-434] Hiding next charge value for users without billing edit permission * [AC-434] Changed canViewSubscription and canEditSubscription to be clearer * [AC-434] Altered BillingSubscriptionItemResponse.amount and BillingSubscriptionUpcomingInvoiceResponse.amount to nullable * [AC-434] Reverted change on BillingSubscriptionItemResponse.amount --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * Updated IsPaidOrgGuard reference from org.CanManageBilling to canEditSubscription --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
194 lines
6.1 KiB
TypeScript
194 lines
6.1 KiB
TypeScript
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
|
import { Router } from "@angular/router";
|
|
import { firstValueFrom } from "rxjs";
|
|
|
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
|
import { ProductType } from "@bitwarden/common/enums";
|
|
import { TreeNode } from "@bitwarden/common/models/domain/tree-node";
|
|
import {
|
|
DialogService,
|
|
SimpleDialogCloseType,
|
|
SimpleDialogOptions,
|
|
SimpleDialogType,
|
|
} from "@bitwarden/components";
|
|
|
|
import {
|
|
CollectionAdminService,
|
|
CollectionAdminView,
|
|
} from "../../../admin-console/organizations/core";
|
|
import { CollectionDialogTabType } from "../../../admin-console/organizations/shared";
|
|
import {
|
|
All,
|
|
RoutedVaultFilterModel,
|
|
Unassigned,
|
|
} from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
|
|
|
@Component({
|
|
selector: "app-org-vault-header",
|
|
templateUrl: "./vault-header.component.html",
|
|
})
|
|
export class VaultHeaderComponent {
|
|
protected All = All;
|
|
protected Unassigned = Unassigned;
|
|
|
|
/**
|
|
* Boolean to determine the loading state of the header.
|
|
* Shows a loading spinner if set to true
|
|
*/
|
|
@Input() loading: boolean;
|
|
|
|
/** Current active fitler */
|
|
@Input() filter: RoutedVaultFilterModel;
|
|
|
|
/** The organization currently being viewed */
|
|
@Input() organization: Organization;
|
|
|
|
/** Currently selected collection */
|
|
@Input() collection?: TreeNode<CollectionAdminView>;
|
|
|
|
/** Emits an event when the new item button is clicked in the header */
|
|
@Output() onAddCipher = new EventEmitter<void>();
|
|
|
|
/** Emits an event when the new collection button is clicked in the header */
|
|
@Output() onAddCollection = new EventEmitter<void>();
|
|
|
|
/** Emits an event when the edit collection button is clicked in the header */
|
|
@Output() onEditCollection = new EventEmitter<{ tab: CollectionDialogTabType }>();
|
|
|
|
/** Emits an event when the delete collection button is clicked in the header */
|
|
@Output() onDeleteCollection = new EventEmitter<void>();
|
|
|
|
protected CollectionDialogTabType = CollectionDialogTabType;
|
|
protected organizations$ = this.organizationService.organizations$;
|
|
|
|
constructor(
|
|
private organizationService: OrganizationService,
|
|
private i18nService: I18nService,
|
|
private dialogService: DialogService,
|
|
private collectionAdminService: CollectionAdminService,
|
|
private router: Router
|
|
) {}
|
|
|
|
get title() {
|
|
if (this.collection !== undefined) {
|
|
return this.collection.node.name;
|
|
}
|
|
|
|
if (this.filter.collectionId === Unassigned) {
|
|
return this.i18nService.t("unassigned");
|
|
}
|
|
|
|
return `${this.organization.name} ${this.i18nService.t("vault").toLowerCase()}`;
|
|
}
|
|
|
|
protected get showBreadcrumbs() {
|
|
return this.filter.collectionId !== undefined && this.filter.collectionId !== All;
|
|
}
|
|
|
|
/**
|
|
* A list of collection filters that form a chain from the organization root to currently selected collection.
|
|
* Begins from the organization root and excludes the currently selected collection.
|
|
*/
|
|
protected get collections() {
|
|
if (this.collection == undefined) {
|
|
return [];
|
|
}
|
|
|
|
const collections = [this.collection];
|
|
while (collections[collections.length - 1].parent != undefined) {
|
|
collections.push(collections[collections.length - 1].parent);
|
|
}
|
|
|
|
return collections
|
|
.slice(1)
|
|
.reverse()
|
|
.map((treeNode) => treeNode.node);
|
|
}
|
|
|
|
private showFreeOrgUpgradeDialog(): void {
|
|
const orgUpgradeSimpleDialogOpts: SimpleDialogOptions = {
|
|
title: this.i18nService.t("upgradeOrganization"),
|
|
content: this.i18nService.t(
|
|
this.organization.canEditSubscription
|
|
? "freeOrgMaxCollectionReachedManageBilling"
|
|
: "freeOrgMaxCollectionReachedNoManageBilling",
|
|
this.organization.maxCollections
|
|
),
|
|
type: SimpleDialogType.PRIMARY,
|
|
};
|
|
|
|
if (this.organization.canEditSubscription) {
|
|
orgUpgradeSimpleDialogOpts.acceptButtonText = this.i18nService.t("upgrade");
|
|
} else {
|
|
orgUpgradeSimpleDialogOpts.acceptButtonText = this.i18nService.t("ok");
|
|
orgUpgradeSimpleDialogOpts.cancelButtonText = null; // hide secondary btn
|
|
}
|
|
|
|
const simpleDialog = this.dialogService.openSimpleDialog(orgUpgradeSimpleDialogOpts);
|
|
|
|
firstValueFrom(simpleDialog.closed).then((result: SimpleDialogCloseType | undefined) => {
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
if (result == SimpleDialogCloseType.ACCEPT && this.organization.canEditSubscription) {
|
|
this.router.navigate(["/organizations", this.organization.id, "billing", "subscription"], {
|
|
queryParams: { upgrade: true },
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
get canEditCollection(): boolean {
|
|
// Only edit collections if not editing "Unassigned"
|
|
if (this.collection === undefined) {
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, check if we can edit the specified collection
|
|
return (
|
|
this.organization.canEditAnyCollection ||
|
|
(this.organization.canEditAssignedCollections && this.collection?.node.assigned)
|
|
);
|
|
}
|
|
|
|
addCipher() {
|
|
this.onAddCipher.emit();
|
|
}
|
|
|
|
async addCollection() {
|
|
if (this.organization.planProductType === ProductType.Free) {
|
|
const collections = await this.collectionAdminService.getAll(this.organization.id);
|
|
if (collections.length === this.organization.maxCollections) {
|
|
this.showFreeOrgUpgradeDialog();
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.onAddCollection.emit();
|
|
}
|
|
|
|
async editCollection(tab: CollectionDialogTabType): Promise<void> {
|
|
this.onEditCollection.emit({ tab });
|
|
}
|
|
|
|
get canDeleteCollection(): boolean {
|
|
// Only delete collections if not deleting "Unassigned"
|
|
if (this.collection === undefined) {
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, check if we can delete the specified collection
|
|
return (
|
|
this.organization?.canDeleteAnyCollection ||
|
|
(this.organization?.canDeleteAssignedCollections && this.collection.node.assigned)
|
|
);
|
|
}
|
|
|
|
deleteCollection() {
|
|
this.onDeleteCollection.emit();
|
|
}
|
|
}
|