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() {
|
||||
if (this.route.snapshot.queryParamMap.get("upgrade")) {
|
||||
this.changePlan();
|
||||
}
|
||||
|
||||
this.route.params
|
||||
.pipe(
|
||||
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 { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.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 { Collection } from "@bitwarden/common/models/domain/collection";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
@ -19,9 +20,11 @@ import {
|
||||
} from "@bitwarden/common/models/response/collection.response";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { CollectionView } from "@bitwarden/common/models/view/collection.view";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { CollectionAddEditComponent } from "./collection-add-edit.component";
|
||||
import { EntityUsersComponent } from "./entity-users.component";
|
||||
import { OrgUpgradeDialogComponent } from "./org-upgrade-dialog/org-upgrade-dialog.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-manage-collections",
|
||||
@ -56,7 +59,8 @@ export class CollectionsComponent implements OnInit {
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private searchService: SearchService,
|
||||
private logService: LogService,
|
||||
private organizationService: OrganizationService
|
||||
private organizationService: OrganizationService,
|
||||
private dialogService: DialogService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -126,6 +130,32 @@ export class CollectionsComponent implements OnInit {
|
||||
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(
|
||||
CollectionAddEditComponent,
|
||||
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="#"
|
||||
appStopClick
|
||||
(click)="groups(u)"
|
||||
*ngIf="accessGroups"
|
||||
*ngIf="organization.useGroups"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-sitemap" aria-hidden="true"></i>
|
||||
{{ "groups" | i18n }}
|
||||
@ -231,7 +231,7 @@
|
||||
href="#"
|
||||
appStopClick
|
||||
(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>
|
||||
{{ "eventLogs" | i18n }}
|
||||
|
@ -20,12 +20,15 @@ import { ValidationService } from "@bitwarden/common/abstractions/validation.ser
|
||||
import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType";
|
||||
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 { OrganizationUserBulkRequest } from "@bitwarden/common/models/request/organization-user-bulk.request";
|
||||
import { OrganizationUserConfirmRequest } from "@bitwarden/common/models/request/organization-user-confirm.request";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { OrganizationUserBulkResponse } from "@bitwarden/common/models/response/organization-user-bulk.response";
|
||||
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/models/response/organization-user.response";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
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 { BulkStatusComponent } from "./bulk/bulk-status.component";
|
||||
import { EntityEventsComponent } from "./entity-events.component";
|
||||
import { OrgUpgradeDialogComponent } from "./org-upgrade-dialog/org-upgrade-dialog.component";
|
||||
import { ResetPasswordComponent } from "./reset-password.component";
|
||||
import { UserAddEditComponent } from "./user-add-edit.component";
|
||||
import { UserGroupsComponent } from "./user-groups.component";
|
||||
@ -65,15 +69,9 @@ export class PeopleComponent
|
||||
userType = OrganizationUserType;
|
||||
userStatusType = OrganizationUserStatusType;
|
||||
|
||||
organizationId: string;
|
||||
organization: Organization;
|
||||
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;
|
||||
callingUserType: OrganizationUserType = null;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
@ -93,7 +91,8 @@ export class PeopleComponent
|
||||
private syncService: SyncService,
|
||||
stateService: StateService,
|
||||
private organizationService: OrganizationService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private dialogService: DialogService
|
||||
) {
|
||||
super(
|
||||
apiService,
|
||||
@ -114,26 +113,23 @@ export class PeopleComponent
|
||||
combineLatest([this.route.params, this.route.queryParams, this.policyService.policies$])
|
||||
.pipe(
|
||||
concatMap(async ([params, qParams, policies]) => {
|
||||
this.organizationId = 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;
|
||||
this.organization = await this.organizationService.get(params.organizationId);
|
||||
|
||||
// Backfill pub/priv key if necessary
|
||||
if (this.canResetPassword && !this.orgHasKeys) {
|
||||
const orgShareKey = await this.cryptoService.getOrgKey(this.organizationId);
|
||||
if (
|
||||
this.organization.canManageUsersPassword &&
|
||||
!this.organization.hasPublicAndPrivateKeys
|
||||
) {
|
||||
const orgShareKey = await this.cryptoService.getOrgKey(this.organization.id);
|
||||
const orgKeys = await this.cryptoService.makeKeyPair(orgShareKey);
|
||||
const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
||||
const response = await this.organizationApiService.updateKeys(
|
||||
this.organizationId,
|
||||
this.organization.id,
|
||||
request
|
||||
);
|
||||
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
|
||||
} else {
|
||||
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||
@ -142,7 +138,7 @@ export class PeopleComponent
|
||||
|
||||
const resetPasswordPolicy = policies
|
||||
.filter((policy) => policy.type === PolicyType.ResetPassword)
|
||||
.find((p) => p.organizationId === this.organizationId);
|
||||
.find((p) => p.organizationId === this.organization.id);
|
||||
this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled;
|
||||
|
||||
await this.load();
|
||||
@ -171,41 +167,41 @@ export class PeopleComponent
|
||||
}
|
||||
|
||||
getUsers(): Promise<ListResponse<OrganizationUserUserDetailsResponse>> {
|
||||
return this.apiService.getOrganizationUsers(this.organizationId);
|
||||
return this.apiService.getOrganizationUsers(this.organization.id);
|
||||
}
|
||||
|
||||
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> {
|
||||
return this.apiService.revokeOrganizationUser(this.organizationId, id);
|
||||
return this.apiService.revokeOrganizationUser(this.organization.id, id);
|
||||
}
|
||||
|
||||
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> {
|
||||
return this.apiService.postOrganizationUserReinvite(this.organizationId, id);
|
||||
return this.apiService.postOrganizationUserReinvite(this.organization.id, id);
|
||||
}
|
||||
|
||||
async confirmUser(
|
||||
user: OrganizationUserUserDetailsResponse,
|
||||
publicKey: Uint8Array
|
||||
): 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 request = new OrganizationUserConfirmRequest();
|
||||
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 {
|
||||
// Hierarchy check
|
||||
let callingUserHasPermission = false;
|
||||
|
||||
switch (this.callingUserType) {
|
||||
switch (this.organization.type) {
|
||||
case OrganizationUserType.Owner:
|
||||
callingUserHasPermission = true;
|
||||
break;
|
||||
@ -221,10 +217,10 @@ export class PeopleComponent
|
||||
|
||||
// Final
|
||||
return (
|
||||
this.canResetPassword &&
|
||||
this.organization.canManageUsersPassword &&
|
||||
callingUserHasPermission &&
|
||||
this.orgUseResetPassword &&
|
||||
this.orgHasKeys &&
|
||||
this.organization.useResetPassword &&
|
||||
this.organization.hasPublicAndPrivateKeys &&
|
||||
orgUser.resetPasswordEnrolled &&
|
||||
this.orgResetPasswordPolicyEnabled &&
|
||||
orgUser.status === OrganizationUserStatusType.Confirmed
|
||||
@ -233,20 +229,51 @@ export class PeopleComponent
|
||||
|
||||
showEnrolledStatus(orgUser: OrganizationUserUserDetailsResponse): boolean {
|
||||
return (
|
||||
this.orgUseResetPassword &&
|
||||
this.organization.useResetPassword &&
|
||||
orgUser.resetPasswordEnrolled &&
|
||||
this.orgResetPasswordPolicyEnabled
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
UserAddEditComponent,
|
||||
this.addEditModalRef,
|
||||
(comp) => {
|
||||
comp.name = this.userNamePipe.transform(user);
|
||||
comp.organizationId = this.organizationId;
|
||||
comp.organizationUserId = user != null ? user.id : null;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.organizationUserId = user?.id || null;
|
||||
comp.usesKeyConnector = user?.usesKeyConnector;
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
comp.onSavedUser.subscribe(() => {
|
||||
@ -278,7 +305,7 @@ export class PeopleComponent
|
||||
this.groupsModalRef,
|
||||
(comp) => {
|
||||
comp.name = this.userNamePipe.transform(user);
|
||||
comp.organizationId = this.organizationId;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.organizationUserId = user != null ? user.id : null;
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
comp.onSavedUser.subscribe(() => {
|
||||
@ -297,7 +324,7 @@ export class PeopleComponent
|
||||
BulkRemoveComponent,
|
||||
this.bulkRemoveModalRef,
|
||||
(comp) => {
|
||||
comp.organizationId = this.organizationId;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.users = this.getCheckedUsers();
|
||||
}
|
||||
);
|
||||
@ -322,7 +349,7 @@ export class PeopleComponent
|
||||
const ref = this.modalService.open(BulkRestoreRevokeComponent, {
|
||||
allowMultipleModals: true,
|
||||
data: {
|
||||
organizationId: this.organizationId,
|
||||
organizationId: this.organization.id,
|
||||
users: this.getCheckedUsers(),
|
||||
isRevoking: isRevoking,
|
||||
},
|
||||
@ -352,7 +379,7 @@ export class PeopleComponent
|
||||
try {
|
||||
const request = new OrganizationUserBulkRequest(filteredUsers.map((user) => user.id));
|
||||
const response = this.apiService.postManyOrganizationUserReinvite(
|
||||
this.organizationId,
|
||||
this.organization.id,
|
||||
request
|
||||
);
|
||||
this.showBulkStatus(
|
||||
@ -376,7 +403,7 @@ export class PeopleComponent
|
||||
BulkConfirmComponent,
|
||||
this.bulkConfirmModalRef,
|
||||
(comp) => {
|
||||
comp.organizationId = this.organizationId;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.users = this.getCheckedUsers();
|
||||
}
|
||||
);
|
||||
@ -388,7 +415,7 @@ export class PeopleComponent
|
||||
async events(user: OrganizationUserUserDetailsResponse) {
|
||||
await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
|
||||
comp.name = this.userNamePipe.transform(user);
|
||||
comp.organizationId = this.organizationId;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.entityId = user.id;
|
||||
comp.showUser = false;
|
||||
comp.entity = "user";
|
||||
@ -402,7 +429,7 @@ export class PeopleComponent
|
||||
(comp) => {
|
||||
comp.name = this.userNamePipe.transform(user);
|
||||
comp.email = user != null ? user.email : null;
|
||||
comp.organizationId = this.organizationId;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.id = user != null ? user.id : null;
|
||||
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { AccessSelectorModule } from "./components/access-selector";
|
||||
import { OrgUpgradeDialogComponent } from "./manage/org-upgrade-dialog/org-upgrade-dialog.component";
|
||||
import { OrganizationsRoutingModule } from "./organization-routing.module";
|
||||
import { SharedOrganizationModule } from "./shared";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedOrganizationModule, AccessSelectorModule, OrganizationsRoutingModule],
|
||||
declarations: [OrgUpgradeDialogComponent],
|
||||
})
|
||||
export class OrganizationModule {}
|
||||
|
@ -5771,5 +5771,41 @@
|
||||
},
|
||||
"memberAccessAll": {
|
||||
"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 { DialogCloseDirective } from "./directives/dialog-close.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({
|
||||
imports: [SharedModule, IconButtonModule, CdkDialogModule],
|
||||
@ -17,8 +17,15 @@ import { SimpleDialogComponent } from "./simple-dialog/simple-dialog.component";
|
||||
DialogTitleContainerDirective,
|
||||
DialogComponent,
|
||||
SimpleDialogComponent,
|
||||
IconDirective,
|
||||
],
|
||||
exports: [
|
||||
CdkDialogModule,
|
||||
DialogComponent,
|
||||
SimpleDialogComponent,
|
||||
DialogCloseDirective,
|
||||
IconDirective,
|
||||
],
|
||||
exports: [CdkDialogModule, DialogComponent, SimpleDialogComponent, DialogCloseDirective],
|
||||
providers: [DialogService],
|
||||
})
|
||||
export class DialogModule {}
|
||||
|
Loading…
Reference in New Issue
Block a user