mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 01:03:35 +00:00
[EC-547] members details dialog improvements (#4161)
* [EC-547] feat: mostly migrate to new CL dialogs * [EC-547] feat: move dialog to separate module * [EC-547] chore: rename to user dialog component * [CL-547] feat: replace footer buttons with CL buttons * [EC-547] chore: move nested checkbox component into dialog module * [EC-547] feat: migrate to async actions and remove form promise * [EC-547] feat: add tab layout * [EC-547] fix: dialog vertical overflow We were using `max-height: 100vh` and `margin: 1rem 0` on the same element which meant that our full height was 100vh + 1rem which pushed the dialog outside of the screen. * [EC-547] feat: change user to member in header * [EC-547] feat: add name to header * [EC-547] feat: add ability to specify initial tab * [EC-547] fix: copy pasta in comments * [EC-547] chore: rename user to member dialog * [EC-547] chore: simplify switch statement
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./member-dialog.component";
|
||||||
|
export * from "./member-dialog.module";
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
|
<bit-dialog dialogSize="large" [disablePadding]="!loading">
|
||||||
|
<span bitDialogTitle>
|
||||||
|
{{ title }}
|
||||||
|
<span class="tw-text-sm tw-normal-case tw-text-muted" *ngIf="!loading && params.name">{{
|
||||||
|
params.name
|
||||||
|
}}</span>
|
||||||
|
<span bitBadge badgeType="secondary" *ngIf="isRevoked">{{ "revoked" | i18n }}</span>
|
||||||
|
</span>
|
||||||
|
<div bitDialogContent>
|
||||||
|
<ng-container *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</ng-container>
|
||||||
|
<bit-tab-group *ngIf="!loading" [selectedIndex]="tabIndex">
|
||||||
|
<bit-tab [label]="'role' | i18n">
|
||||||
|
<ng-container *ngIf="!editMode">
|
||||||
|
<p>{{ "inviteUserDesc" | i18n }}</p>
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label for="emails">{{ "email" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="emails"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
name="Emails"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="emails"
|
||||||
|
required
|
||||||
|
appAutoFocus
|
||||||
|
/>
|
||||||
|
<small class="text-muted">{{ "inviteMultipleEmailDesc" | i18n: "20" }}</small>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<h3>
|
||||||
|
{{ "userType" | i18n }}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
href="https://bitwarden.com/help/user-types-access-control/"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="userType"
|
||||||
|
id="userTypeUser"
|
||||||
|
[value]="organizationUserType.User"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="type"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="userTypeUser">
|
||||||
|
{{ "user" | i18n }}
|
||||||
|
<small>{{ "userDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="userType"
|
||||||
|
id="userTypeManager"
|
||||||
|
[value]="organizationUserType.Manager"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="type"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="userTypeManager">
|
||||||
|
{{ "manager" | i18n }}
|
||||||
|
<small>{{ "managerDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="userType"
|
||||||
|
id="userTypeAdmin"
|
||||||
|
[value]="organizationUserType.Admin"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="type"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="userTypeAdmin">
|
||||||
|
{{ "admin" | i18n }}
|
||||||
|
<small>{{ "adminDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="userType"
|
||||||
|
id="userTypeOwner"
|
||||||
|
[value]="organizationUserType.Owner"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="type"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="userTypeOwner">
|
||||||
|
{{ "owner" | i18n }}
|
||||||
|
<small>{{ "ownerDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="userType"
|
||||||
|
id="userTypeCustom"
|
||||||
|
[value]="organizationUserType.Custom"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="type"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="userTypeCustom">
|
||||||
|
{{ "custom" | i18n }}
|
||||||
|
<small>{{ "customDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="customUserTypeSelected">
|
||||||
|
<h3 class="mt-4 d-flex">
|
||||||
|
{{ "permissions" | i18n }}
|
||||||
|
</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="font-weight-bold mb-0">{{ "managerPermissions" | i18n }}</label>
|
||||||
|
<hr class="my-0 mr-2" />
|
||||||
|
<app-nested-checkbox
|
||||||
|
parentId="manageAssignedCollections"
|
||||||
|
[checkboxes]="manageAssignedCollectionsCheckboxes"
|
||||||
|
>
|
||||||
|
</app-nested-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="font-weight-bold mb-0">{{ "adminPermissions" | i18n }}</label>
|
||||||
|
<hr class="my-0 mr-2" />
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="accessEventLogs"
|
||||||
|
id="accessEventLogs"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.accessEventLogs"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="accessEventLogs">
|
||||||
|
{{ "accessEventLogs" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="accessImportExport"
|
||||||
|
id="accessImportExport"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.accessImportExport"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="accessImportExport">
|
||||||
|
{{ "accessImportExport" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="accessReports"
|
||||||
|
id="accessReports"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.accessReports"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="accessReports">
|
||||||
|
{{ "accessReports" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<app-nested-checkbox
|
||||||
|
parentId="manageAllCollections"
|
||||||
|
[checkboxes]="manageAllCollectionsCheckboxes"
|
||||||
|
>
|
||||||
|
</app-nested-checkbox>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="manageGroups"
|
||||||
|
id="manageGroups"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.manageGroups"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="manageGroups">
|
||||||
|
{{ "manageGroups" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="manageSso"
|
||||||
|
id="managePolicies"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.manageSso"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="manageSso">
|
||||||
|
{{ "manageSso" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="managePolicies"
|
||||||
|
id="managePolicies"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.managePolicies"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="managePolicies">
|
||||||
|
{{ "managePolicies" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="manageUsers"
|
||||||
|
id="manageUsers"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.manageUsers"
|
||||||
|
(change)="handleDependentPermissions()"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="manageUsers">
|
||||||
|
{{ "manageUsers" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="form-check mt-1 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
name="manageResetPassword"
|
||||||
|
id="manageResetPassword"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="permissions.manageResetPassword"
|
||||||
|
(change)="handleDependentPermissions()"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label font-weight-normal" for="manageResetPassword">
|
||||||
|
{{ "manageResetPassword" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<h3 class="mt-4 d-flex">
|
||||||
|
<div class="mb-3">
|
||||||
|
{{ "accessControl" | i18n }}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
href="https://bitwarden.com/help/user-types-access-control/#access-control"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto" *ngIf="access === 'selected' && collections && collections.length">
|
||||||
|
<button type="button" (click)="selectAll(true)" class="btn btn-link btn-sm py-0">
|
||||||
|
{{ "selectAll" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button type="button" (click)="selectAll(false)" class="btn btn-link btn-sm py-0">
|
||||||
|
{{ "unselectAll" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</h3>
|
||||||
|
<div class="form-group" [ngClass]="{ 'mb-0': access !== 'selected' }">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="access"
|
||||||
|
id="accessAll"
|
||||||
|
value="all"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="access"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="accessAll">
|
||||||
|
{{ "userAccessAllItems" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="access"
|
||||||
|
id="accessSelected"
|
||||||
|
value="selected"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="access"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="accessSelected">
|
||||||
|
{{ "userAccessSelectedCollections" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="access === 'selected'">
|
||||||
|
<div *ngIf="!collections || !collections.length">
|
||||||
|
{{ "noCollectionsInList" | i18n }}
|
||||||
|
</div>
|
||||||
|
<table
|
||||||
|
class="table table-hover table-list mb-0"
|
||||||
|
*ngIf="collections && collections.length"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
<th>{{ "name" | i18n }}</th>
|
||||||
|
<th width="100" class="text-center">{{ "hidePasswords" | i18n }}</th>
|
||||||
|
<th width="100" class="text-center">{{ "readOnly" | i18n }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let c of collections; let i = index">
|
||||||
|
<td class="table-list-checkbox" (click)="check(c)">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="c.checked"
|
||||||
|
name="Collection[{{ i }}].Checked"
|
||||||
|
appStopProp
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td (click)="check(c)">
|
||||||
|
{{ c.name }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="c.hidePasswords"
|
||||||
|
name="Collection[{{ i }}].HidePasswords"
|
||||||
|
[disabled]="!c.checked"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
[ngModelOptions]="{ standalone: true }"
|
||||||
|
[(ngModel)]="c.readOnly"
|
||||||
|
name="Collection[{{ i }}].ReadOnly"
|
||||||
|
[disabled]="!c.checked"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ng-container>
|
||||||
|
</bit-tab>
|
||||||
|
<bit-tab [label]="'groups' | i18n">Groups</bit-tab>
|
||||||
|
<bit-tab [label]="'collections' | i18n">Collections</bit-tab>
|
||||||
|
</bit-tab-group>
|
||||||
|
</div>
|
||||||
|
<div bitDialogFooter class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button type="submit" bitButton bitFormButton buttonType="primary" [disabled]="loading">
|
||||||
|
{{ "save" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="secondary"
|
||||||
|
(click)="cancel()"
|
||||||
|
[disabled]="loading"
|
||||||
|
>
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
<div class="tw-ml-auto">
|
||||||
|
<button
|
||||||
|
*ngIf="editMode && isRevoked"
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[bitAction]="restore"
|
||||||
|
[disabled]="loading"
|
||||||
|
>
|
||||||
|
{{ "restoreAccess" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
*ngIf="editMode && !isRevoked"
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[bitAction]="revoke"
|
||||||
|
[disabled]="loading"
|
||||||
|
>
|
||||||
|
{{ "revokeAccess" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
*ngIf="editMode"
|
||||||
|
type="button"
|
||||||
|
bitIconButton="bwi-trash"
|
||||||
|
buttonType="danger"
|
||||||
|
bitFormButton
|
||||||
|
[appA11yTitle]="'delete' | i18n"
|
||||||
|
[bitAction]="delete"
|
||||||
|
[disabled]="loading"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</bit-dialog>
|
||||||
|
</form>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder } from "@angular/forms";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||||
@@ -15,21 +17,35 @@ import { OrganizationUserUpdateRequest } from "@bitwarden/common/models/request/
|
|||||||
import { SelectionReadOnlyRequest } from "@bitwarden/common/models/request/selection-read-only.request";
|
import { SelectionReadOnlyRequest } from "@bitwarden/common/models/request/selection-read-only.request";
|
||||||
import { CollectionDetailsResponse } from "@bitwarden/common/models/response/collection.response";
|
import { CollectionDetailsResponse } from "@bitwarden/common/models/response/collection.response";
|
||||||
import { CollectionView } from "@bitwarden/common/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/models/view/collection.view";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
export enum MemberDialogTab {
|
||||||
|
Role = 0,
|
||||||
|
Groups = 1,
|
||||||
|
Collections = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MemberDialogParams {
|
||||||
|
name: string;
|
||||||
|
organizationId: string;
|
||||||
|
organizationUserId: string;
|
||||||
|
usesKeyConnector: boolean;
|
||||||
|
initialTab?: MemberDialogTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MemberDialogResult {
|
||||||
|
Saved = "saved",
|
||||||
|
Canceled = "canceled",
|
||||||
|
Deleted = "deleted",
|
||||||
|
Revoked = "revoked",
|
||||||
|
Restored = "restored",
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-user-add-edit",
|
selector: "app-member-dialog",
|
||||||
templateUrl: "user-add-edit.component.html",
|
templateUrl: "member-dialog.component.html",
|
||||||
})
|
})
|
||||||
export class UserAddEditComponent implements OnInit {
|
export class MemberDialogComponent implements OnInit {
|
||||||
@Input() name: string;
|
|
||||||
@Input() organizationUserId: string;
|
|
||||||
@Input() organizationId: string;
|
|
||||||
@Input() usesKeyConnector = false;
|
|
||||||
@Output() onSavedUser = new EventEmitter();
|
|
||||||
@Output() onDeletedUser = new EventEmitter();
|
|
||||||
@Output() onRevokedUser = new EventEmitter();
|
|
||||||
@Output() onRestoredUser = new EventEmitter();
|
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
editMode = false;
|
editMode = false;
|
||||||
isRevoked = false;
|
isRevoked = false;
|
||||||
@@ -40,10 +56,12 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
showCustom = false;
|
showCustom = false;
|
||||||
access: "all" | "selected" = "selected";
|
access: "all" | "selected" = "selected";
|
||||||
collections: CollectionView[] = [];
|
collections: CollectionView[] = [];
|
||||||
formPromise: Promise<any>;
|
|
||||||
deletePromise: Promise<any>;
|
|
||||||
organizationUserType = OrganizationUserType;
|
organizationUserType = OrganizationUserType;
|
||||||
|
|
||||||
|
protected tabIndex: MemberDialogTab;
|
||||||
|
// Stub, to be filled out in upcoming PRs
|
||||||
|
protected formGroup = this.formBuilder.group({});
|
||||||
|
|
||||||
manageAllCollectionsCheckboxes = [
|
manageAllCollectionsCheckboxes = [
|
||||||
{
|
{
|
||||||
id: "createNewCollections",
|
id: "createNewCollections",
|
||||||
@@ -80,24 +98,28 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(DIALOG_DATA) protected params: MemberDialogParams,
|
||||||
|
private dialogRef: DialogRef<MemberDialogResult>,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private logService: LogService
|
private logService: LogService,
|
||||||
|
private formBuilder: FormBuilder
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.editMode = this.loading = this.organizationUserId != null;
|
this.editMode = this.loading = this.params.organizationUserId != null;
|
||||||
|
this.tabIndex = this.params.initialTab ?? MemberDialogTab.Role;
|
||||||
await this.loadCollections();
|
await this.loadCollections();
|
||||||
|
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
this.editMode = true;
|
this.editMode = true;
|
||||||
this.title = this.i18nService.t("editUser");
|
this.title = this.i18nService.t("editMember");
|
||||||
try {
|
try {
|
||||||
const user = await this.apiService.getOrganizationUser(
|
const user = await this.apiService.getOrganizationUser(
|
||||||
this.organizationId,
|
this.params.organizationId,
|
||||||
this.organizationUserId
|
this.params.organizationUserId
|
||||||
);
|
);
|
||||||
this.access = user.accessAll ? "all" : "selected";
|
this.access = user.accessAll ? "all" : "selected";
|
||||||
this.type = user.type;
|
this.type = user.type;
|
||||||
@@ -119,14 +141,14 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.title = this.i18nService.t("inviteUser");
|
this.title = this.i18nService.t("inviteMember");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadCollections() {
|
async loadCollections() {
|
||||||
const response = await this.apiService.getCollections(this.organizationId);
|
const response = await this.apiService.getCollections(this.params.organizationId);
|
||||||
const collections = response.data.map(
|
const collections = response.data.map(
|
||||||
(r) => new Collection(new CollectionData(r as CollectionDetailsResponse))
|
(r) => new Collection(new CollectionData(r as CollectionDetailsResponse))
|
||||||
);
|
);
|
||||||
@@ -162,7 +184,7 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
submit = async () => {
|
||||||
let collections: SelectionReadOnlyRequest[] = null;
|
let collections: SelectionReadOnlyRequest[] = null;
|
||||||
if (this.access !== "all") {
|
if (this.access !== "all") {
|
||||||
collections = this.collections
|
collections = this.collections
|
||||||
@@ -180,9 +202,9 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
request.permissions ?? new PermissionsApi(),
|
request.permissions ?? new PermissionsApi(),
|
||||||
request.type !== OrganizationUserType.Custom
|
request.type !== OrganizationUserType.Custom
|
||||||
);
|
);
|
||||||
this.formPromise = this.apiService.putOrganizationUser(
|
await this.apiService.putOrganizationUser(
|
||||||
this.organizationId,
|
this.params.organizationId,
|
||||||
this.organizationUserId,
|
this.params.organizationUserId,
|
||||||
request
|
request
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -195,31 +217,31 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
request.type !== OrganizationUserType.Custom
|
request.type !== OrganizationUserType.Custom
|
||||||
);
|
);
|
||||||
request.collections = collections;
|
request.collections = collections;
|
||||||
this.formPromise = this.apiService.postOrganizationUserInvite(this.organizationId, request);
|
await this.apiService.postOrganizationUserInvite(this.params.organizationId, request);
|
||||||
}
|
}
|
||||||
await this.formPromise;
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"success",
|
"success",
|
||||||
null,
|
null,
|
||||||
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name)
|
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name)
|
||||||
);
|
);
|
||||||
this.onSavedUser.emit();
|
this.close(MemberDialogResult.Saved);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async delete() {
|
delete = async () => {
|
||||||
if (!this.editMode) {
|
if (!this.editMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = this.usesKeyConnector
|
const message = this.params.usesKeyConnector
|
||||||
? "removeUserConfirmationKeyConnector"
|
? "removeUserConfirmationKeyConnector"
|
||||||
: "removeOrgUserConfirmation";
|
: "removeOrgUserConfirmation";
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t(message),
|
this.i18nService.t(message),
|
||||||
this.i18nService.t("removeUserIdAccess", this.name),
|
this.i18nService.t("removeUserIdAccess", this.params.name),
|
||||||
this.i18nService.t("yes"),
|
this.i18nService.t("yes"),
|
||||||
this.i18nService.t("no"),
|
this.i18nService.t("no"),
|
||||||
"warning"
|
"warning"
|
||||||
@@ -229,30 +251,30 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.deletePromise = this.apiService.deleteOrganizationUser(
|
await this.apiService.deleteOrganizationUser(
|
||||||
this.organizationId,
|
this.params.organizationId,
|
||||||
this.organizationUserId
|
this.params.organizationUserId
|
||||||
);
|
);
|
||||||
await this.deletePromise;
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"success",
|
"success",
|
||||||
null,
|
null,
|
||||||
this.i18nService.t("removedUserId", this.name)
|
this.i18nService.t("removedUserId", this.params.name)
|
||||||
);
|
);
|
||||||
this.onDeletedUser.emit();
|
this.close(MemberDialogResult.Deleted);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async revoke() {
|
revoke = async () => {
|
||||||
if (!this.editMode) {
|
if (!this.editMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t("revokeUserConfirmation"),
|
this.i18nService.t("revokeUserConfirmation"),
|
||||||
this.i18nService.t("revokeUserId", this.name),
|
this.i18nService.t("revokeUserId", this.params.name),
|
||||||
this.i18nService.t("revokeAccess"),
|
this.i18nService.t("revokeAccess"),
|
||||||
this.i18nService.t("cancel"),
|
this.i18nService.t("cancel"),
|
||||||
"warning"
|
"warning"
|
||||||
@@ -262,43 +284,63 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.formPromise = this.apiService.revokeOrganizationUser(
|
await this.apiService.revokeOrganizationUser(
|
||||||
this.organizationId,
|
this.params.organizationId,
|
||||||
this.organizationUserId
|
this.params.organizationUserId
|
||||||
);
|
);
|
||||||
await this.formPromise;
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"success",
|
"success",
|
||||||
null,
|
null,
|
||||||
this.i18nService.t("revokedUserId", this.name)
|
this.i18nService.t("revokedUserId", this.params.name)
|
||||||
);
|
);
|
||||||
this.isRevoked = true;
|
this.isRevoked = true;
|
||||||
this.onRevokedUser.emit();
|
this.close(MemberDialogResult.Revoked);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async restore() {
|
restore = async () => {
|
||||||
if (!this.editMode) {
|
if (!this.editMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.formPromise = this.apiService.restoreOrganizationUser(
|
await this.apiService.restoreOrganizationUser(
|
||||||
this.organizationId,
|
this.params.organizationId,
|
||||||
this.organizationUserId
|
this.params.organizationUserId
|
||||||
);
|
);
|
||||||
await this.formPromise;
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"success",
|
"success",
|
||||||
null,
|
null,
|
||||||
this.i18nService.t("restoredUserId", this.name)
|
this.i18nService.t("restoredUserId", this.params.name)
|
||||||
);
|
);
|
||||||
this.isRevoked = false;
|
this.isRevoked = false;
|
||||||
this.onRestoredUser.emit();
|
this.close(MemberDialogResult.Restored);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected async cancel() {
|
||||||
|
this.close(MemberDialogResult.Canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private close(result: MemberDialogResult) {
|
||||||
|
this.dialogRef.close(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strongly typed helper to open a UserDialog
|
||||||
|
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||||
|
* @param config Configuration for the dialog
|
||||||
|
*/
|
||||||
|
export function openUserAddEditDialog(
|
||||||
|
dialogService: DialogService,
|
||||||
|
config: DialogConfig<MemberDialogParams>
|
||||||
|
) {
|
||||||
|
return dialogService.open<MemberDialogResult, MemberDialogParams>(MemberDialogComponent, config);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../../shared/shared.module";
|
||||||
|
|
||||||
|
import { MemberDialogComponent } from "./member-dialog.component";
|
||||||
|
import { NestedCheckboxComponent } from "./nested-checkbox.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MemberDialogComponent, NestedCheckboxComponent],
|
||||||
|
imports: [SharedModule],
|
||||||
|
exports: [MemberDialogComponent],
|
||||||
|
})
|
||||||
|
export class UserDialogModule {}
|
||||||
@@ -4,9 +4,10 @@ import { NgModule } from "@angular/core";
|
|||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../shared";
|
||||||
|
|
||||||
import { EntityUsersComponent } from "./entity-users.component";
|
import { EntityUsersComponent } from "./entity-users.component";
|
||||||
|
import { UserDialogModule } from "./member-dialog";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedModule, ScrollingModule],
|
imports: [SharedModule, ScrollingModule, UserDialogModule],
|
||||||
declarations: [EntityUsersComponent],
|
declarations: [EntityUsersComponent],
|
||||||
exports: [EntityUsersComponent],
|
exports: [EntityUsersComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatest, concatMap, Subject, takeUntil } from "rxjs";
|
import { combineLatest, concatMap, lastValueFrom, Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||||
@@ -26,6 +26,7 @@ import { OrganizationUserConfirmRequest } from "@bitwarden/common/models/request
|
|||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { OrganizationUserBulkResponse } from "@bitwarden/common/models/response/organization-user-bulk.response";
|
import { OrganizationUserBulkResponse } from "@bitwarden/common/models/response/organization-user-bulk.response";
|
||||||
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/models/response/organization-user.response";
|
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/models/response/organization-user.response";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { BasePeopleComponent } from "../../common/base.people.component";
|
import { BasePeopleComponent } from "../../common/base.people.component";
|
||||||
|
|
||||||
@@ -34,8 +35,8 @@ import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
|||||||
import { BulkRestoreRevokeComponent } from "./bulk/bulk-restore-revoke.component";
|
import { BulkRestoreRevokeComponent } from "./bulk/bulk-restore-revoke.component";
|
||||||
import { BulkStatusComponent } from "./bulk/bulk-status.component";
|
import { BulkStatusComponent } from "./bulk/bulk-status.component";
|
||||||
import { EntityEventsComponent } from "./entity-events.component";
|
import { EntityEventsComponent } from "./entity-events.component";
|
||||||
|
import { openUserAddEditDialog, MemberDialogResult } from "./member-dialog/member-dialog.component";
|
||||||
import { ResetPasswordComponent } from "./reset-password.component";
|
import { ResetPasswordComponent } from "./reset-password.component";
|
||||||
import { UserAddEditComponent } from "./user-add-edit.component";
|
|
||||||
import { UserGroupsComponent } from "./user-groups.component";
|
import { UserGroupsComponent } from "./user-groups.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -46,7 +47,6 @@ export class PeopleComponent
|
|||||||
extends BasePeopleComponent<OrganizationUserUserDetailsResponse>
|
extends BasePeopleComponent<OrganizationUserUserDetailsResponse>
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
@ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
|
|
||||||
@ViewChild("groupsTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("groupsTemplate", { read: ViewContainerRef, static: true })
|
||||||
groupsModalRef: ViewContainerRef;
|
groupsModalRef: ViewContainerRef;
|
||||||
@ViewChild("eventsTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("eventsTemplate", { read: ViewContainerRef, static: true })
|
||||||
@@ -93,7 +93,8 @@ export class PeopleComponent
|
|||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
stateService: StateService,
|
stateService: StateService,
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
private dialogService: DialogService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
apiService,
|
apiService,
|
||||||
@@ -240,36 +241,26 @@ export class PeopleComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
async edit(user: OrganizationUserUserDetailsResponse) {
|
async edit(user: OrganizationUserUserDetailsResponse) {
|
||||||
const [modal] = await this.modalService.openViewRef(
|
const dialog = openUserAddEditDialog(this.dialogService, {
|
||||||
UserAddEditComponent,
|
data: {
|
||||||
this.addEditModalRef,
|
name: this.userNamePipe.transform(user),
|
||||||
(comp) => {
|
organizationId: this.organizationId,
|
||||||
comp.name = this.userNamePipe.transform(user);
|
organizationUserId: user != null ? user.id : null,
|
||||||
comp.organizationId = this.organizationId;
|
usesKeyConnector: user?.usesKeyConnector,
|
||||||
comp.organizationUserId = user != null ? user.id : null;
|
},
|
||||||
comp.usesKeyConnector = user?.usesKeyConnector;
|
});
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
comp.onSavedUser.subscribe(() => {
|
const result = await lastValueFrom(dialog.closed);
|
||||||
modal.close();
|
switch (result) {
|
||||||
this.load();
|
case MemberDialogResult.Deleted:
|
||||||
});
|
this.removeUser(user);
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
break;
|
||||||
comp.onDeletedUser.subscribe(() => {
|
case MemberDialogResult.Saved:
|
||||||
modal.close();
|
case MemberDialogResult.Revoked:
|
||||||
this.removeUser(user);
|
case MemberDialogResult.Restored:
|
||||||
});
|
this.load();
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
break;
|
||||||
comp.onRevokedUser.subscribe(() => {
|
}
|
||||||
modal.close();
|
|
||||||
this.load();
|
|
||||||
});
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
comp.onRestoredUser.subscribe(() => {
|
|
||||||
modal.close();
|
|
||||||
this.load();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async groups(user: OrganizationUserUserDetailsResponse) {
|
async groups(user: OrganizationUserUserDetailsResponse) {
|
||||||
|
|||||||
@@ -1,438 +0,0 @@
|
|||||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="userAddEditTitle">
|
|
||||||
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
|
|
||||||
<form
|
|
||||||
class="modal-content"
|
|
||||||
#form
|
|
||||||
(ngSubmit)="submit()"
|
|
||||||
[appApiAction]="formPromise"
|
|
||||||
ngNativeValidate
|
|
||||||
>
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title" id="userAddEditTitle">
|
|
||||||
{{ title }}
|
|
||||||
<small class="text-muted" *ngIf="name">{{ name }}</small>
|
|
||||||
<span bitBadge badgeType="secondary" *ngIf="isRevoked">{{ "revoked" | i18n }}</span>
|
|
||||||
</h1>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
appA11yTitle="{{ 'close' | i18n }}"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body" *ngIf="loading">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin text-muted"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body" *ngIf="!loading">
|
|
||||||
<ng-container *ngIf="!editMode">
|
|
||||||
<p>{{ "inviteUserDesc" | i18n }}</p>
|
|
||||||
<div class="form-group mb-4">
|
|
||||||
<label for="emails">{{ "email" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="emails"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
name="Emails"
|
|
||||||
[(ngModel)]="emails"
|
|
||||||
required
|
|
||||||
appAutoFocus
|
|
||||||
/>
|
|
||||||
<small class="text-muted">{{ "inviteMultipleEmailDesc" | i18n: "20" }}</small>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<h3>
|
|
||||||
{{ "userType" | i18n }}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
|
||||||
href="https://bitwarden.com/help/user-types-access-control/"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</h3>
|
|
||||||
<div class="form-check mt-2 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="userType"
|
|
||||||
id="userTypeUser"
|
|
||||||
[value]="organizationUserType.User"
|
|
||||||
[(ngModel)]="type"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="userTypeUser">
|
|
||||||
{{ "user" | i18n }}
|
|
||||||
<small>{{ "userDesc" | i18n }}</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check mt-2 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="userType"
|
|
||||||
id="userTypeManager"
|
|
||||||
[value]="organizationUserType.Manager"
|
|
||||||
[(ngModel)]="type"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="userTypeManager">
|
|
||||||
{{ "manager" | i18n }}
|
|
||||||
<small>{{ "managerDesc" | i18n }}</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check mt-2 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="userType"
|
|
||||||
id="userTypeAdmin"
|
|
||||||
[value]="organizationUserType.Admin"
|
|
||||||
[(ngModel)]="type"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="userTypeAdmin">
|
|
||||||
{{ "admin" | i18n }}
|
|
||||||
<small>{{ "adminDesc" | i18n }}</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check mt-2 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="userType"
|
|
||||||
id="userTypeOwner"
|
|
||||||
[value]="organizationUserType.Owner"
|
|
||||||
[(ngModel)]="type"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="userTypeOwner">
|
|
||||||
{{ "owner" | i18n }}
|
|
||||||
<small>{{ "ownerDesc" | i18n }}</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check mt-2 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="userType"
|
|
||||||
id="userTypeCustom"
|
|
||||||
[value]="organizationUserType.Custom"
|
|
||||||
[(ngModel)]="type"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="userTypeCustom">
|
|
||||||
{{ "custom" | i18n }}
|
|
||||||
<small>{{ "customDesc" | i18n }}</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<ng-container *ngIf="customUserTypeSelected">
|
|
||||||
<h3 class="mt-4 d-flex">
|
|
||||||
{{ "permissions" | i18n }}
|
|
||||||
</h3>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="font-weight-bold mb-0">{{ "managerPermissions" | i18n }}</label>
|
|
||||||
<hr class="my-0 mr-2" />
|
|
||||||
<app-nested-checkbox
|
|
||||||
parentId="manageAssignedCollections"
|
|
||||||
[checkboxes]="manageAssignedCollectionsCheckboxes"
|
|
||||||
>
|
|
||||||
</app-nested-checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="font-weight-bold mb-0">{{ "adminPermissions" | i18n }}</label>
|
|
||||||
<hr class="my-0 mr-2" />
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="accessEventLogs"
|
|
||||||
id="accessEventLogs"
|
|
||||||
[(ngModel)]="permissions.accessEventLogs"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="accessEventLogs">
|
|
||||||
{{ "accessEventLogs" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="accessImportExport"
|
|
||||||
id="accessImportExport"
|
|
||||||
[(ngModel)]="permissions.accessImportExport"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="accessImportExport">
|
|
||||||
{{ "accessImportExport" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="accessReports"
|
|
||||||
id="accessReports"
|
|
||||||
[(ngModel)]="permissions.accessReports"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="accessReports">
|
|
||||||
{{ "accessReports" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<app-nested-checkbox
|
|
||||||
parentId="manageAllCollections"
|
|
||||||
[checkboxes]="manageAllCollectionsCheckboxes"
|
|
||||||
>
|
|
||||||
</app-nested-checkbox>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="manageGroups"
|
|
||||||
id="manageGroups"
|
|
||||||
[(ngModel)]="permissions.manageGroups"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="manageGroups">
|
|
||||||
{{ "manageGroups" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="manageSso"
|
|
||||||
id="managePolicies"
|
|
||||||
[(ngModel)]="permissions.manageSso"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="manageSso">
|
|
||||||
{{ "manageSso" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="managePolicies"
|
|
||||||
id="managePolicies"
|
|
||||||
[(ngModel)]="permissions.managePolicies"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="managePolicies">
|
|
||||||
{{ "managePolicies" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="manageUsers"
|
|
||||||
id="manageUsers"
|
|
||||||
[(ngModel)]="permissions.manageUsers"
|
|
||||||
(change)="handleDependentPermissions()"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="manageUsers">
|
|
||||||
{{ "manageUsers" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="form-check mt-1 form-check-block">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
name="manageResetPassword"
|
|
||||||
id="manageResetPassword"
|
|
||||||
[(ngModel)]="permissions.manageResetPassword"
|
|
||||||
(change)="handleDependentPermissions()"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label font-weight-normal" for="manageResetPassword">
|
|
||||||
{{ "manageResetPassword" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<h3 class="mt-4 d-flex">
|
|
||||||
<div class="mb-3">
|
|
||||||
{{ "accessControl" | i18n }}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
|
||||||
href="https://bitwarden.com/help/user-types-access-control/#access-control"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="ml-auto" *ngIf="access === 'selected' && collections && collections.length">
|
|
||||||
<button type="button" (click)="selectAll(true)" class="btn btn-link btn-sm py-0">
|
|
||||||
{{ "selectAll" | i18n }}
|
|
||||||
</button>
|
|
||||||
<button type="button" (click)="selectAll(false)" class="btn btn-link btn-sm py-0">
|
|
||||||
{{ "unselectAll" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</h3>
|
|
||||||
<div class="form-group" [ngClass]="{ 'mb-0': access !== 'selected' }">
|
|
||||||
<div class="form-check">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="access"
|
|
||||||
id="accessAll"
|
|
||||||
value="all"
|
|
||||||
[(ngModel)]="access"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="accessAll">
|
|
||||||
{{ "userAccessAllItems" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="access"
|
|
||||||
id="accessSelected"
|
|
||||||
value="selected"
|
|
||||||
[(ngModel)]="access"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="accessSelected">
|
|
||||||
{{ "userAccessSelectedCollections" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ng-container *ngIf="access === 'selected'">
|
|
||||||
<div *ngIf="!collections || !collections.length">
|
|
||||||
{{ "noCollectionsInList" | i18n }}
|
|
||||||
</div>
|
|
||||||
<table
|
|
||||||
class="table table-hover table-list mb-0"
|
|
||||||
*ngIf="collections && collections.length"
|
|
||||||
>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th> </th>
|
|
||||||
<th>{{ "name" | i18n }}</th>
|
|
||||||
<th width="100" class="text-center">{{ "hidePasswords" | i18n }}</th>
|
|
||||||
<th width="100" class="text-center">{{ "readOnly" | i18n }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let c of collections; let i = index">
|
|
||||||
<td class="table-list-checkbox" (click)="check(c)">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
[(ngModel)]="c.checked"
|
|
||||||
name="Collection[{{ i }}].Checked"
|
|
||||||
appStopProp
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td (click)="check(c)">
|
|
||||||
{{ c.name }}
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
[(ngModel)]="c.hidePasswords"
|
|
||||||
name="Collection[{{ i }}].HidePasswords"
|
|
||||||
[disabled]="!c.checked"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
[(ngModel)]="c.readOnly"
|
|
||||||
name="Collection[{{ i }}].ReadOnly"
|
|
||||||
[disabled]="!c.checked"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
|
||||||
<span>{{ "save" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
|
||||||
{{ "cancel" | i18n }}
|
|
||||||
</button>
|
|
||||||
<div class="ml-auto">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
(click)="restore()"
|
|
||||||
class="btn btn-outline-secondary"
|
|
||||||
*ngIf="editMode && isRevoked"
|
|
||||||
[disabled]="form.loading"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
|
||||||
[hidden]="!form.loading"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span>{{ "restoreAccess" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
(click)="revoke()"
|
|
||||||
class="btn btn-outline-secondary"
|
|
||||||
*ngIf="editMode && !isRevoked"
|
|
||||||
[disabled]="form.loading"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
|
||||||
[hidden]="!form.loading"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span>{{ "revokeAccess" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
#deleteBtn
|
|
||||||
type="button"
|
|
||||||
(click)="delete()"
|
|
||||||
class="btn btn-outline-danger"
|
|
||||||
appA11yTitle="{{ 'delete' | i18n }}"
|
|
||||||
*ngIf="editMode"
|
|
||||||
[disabled]="deleteBtn.loading"
|
|
||||||
[appApiAction]="deletePromise"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-trash bwi-lg bwi-fw"
|
|
||||||
[hidden]="deleteBtn.loading"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
|
||||||
[hidden]="!deleteBtn.loading"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -19,7 +19,6 @@ import { UpdatePasswordComponent } from "../accounts/update-password.component";
|
|||||||
import { UpdateTempPasswordComponent } from "../accounts/update-temp-password.component";
|
import { UpdateTempPasswordComponent } from "../accounts/update-temp-password.component";
|
||||||
import { VerifyEmailTokenComponent } from "../accounts/verify-email-token.component";
|
import { VerifyEmailTokenComponent } from "../accounts/verify-email-token.component";
|
||||||
import { VerifyRecoverDeleteComponent } from "../accounts/verify-recover-delete.component";
|
import { VerifyRecoverDeleteComponent } from "../accounts/verify-recover-delete.component";
|
||||||
import { NestedCheckboxComponent } from "../components/nested-checkbox.component";
|
|
||||||
import { OrganizationSwitcherComponent } from "../components/organization-switcher.component";
|
import { OrganizationSwitcherComponent } from "../components/organization-switcher.component";
|
||||||
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
||||||
import { PremiumBadgeComponent } from "../components/premium-badge.component";
|
import { PremiumBadgeComponent } from "../components/premium-badge.component";
|
||||||
@@ -40,7 +39,6 @@ import { EventsComponent as OrgEventsComponent } from "../organizations/manage/e
|
|||||||
import { ManageComponent as OrgManageComponent } from "../organizations/manage/manage.component";
|
import { ManageComponent as OrgManageComponent } from "../organizations/manage/manage.component";
|
||||||
import { PeopleComponent as OrgPeopleComponent } from "../organizations/manage/people.component";
|
import { PeopleComponent as OrgPeopleComponent } from "../organizations/manage/people.component";
|
||||||
import { ResetPasswordComponent as OrgResetPasswordComponent } from "../organizations/manage/reset-password.component";
|
import { ResetPasswordComponent as OrgResetPasswordComponent } from "../organizations/manage/reset-password.component";
|
||||||
import { UserAddEditComponent as OrgUserAddEditComponent } from "../organizations/manage/user-add-edit.component";
|
|
||||||
import { UserConfirmComponent as OrgUserConfirmComponent } from "../organizations/manage/user-confirm.component";
|
import { UserConfirmComponent as OrgUserConfirmComponent } from "../organizations/manage/user-confirm.component";
|
||||||
import { AcceptFamilySponsorshipComponent } from "../organizations/sponsorships/accept-family-sponsorship.component";
|
import { AcceptFamilySponsorshipComponent } from "../organizations/sponsorships/accept-family-sponsorship.component";
|
||||||
import { FamiliesForEnterpriseSetupComponent } from "../organizations/sponsorships/families-for-enterprise-setup.component";
|
import { FamiliesForEnterpriseSetupComponent } from "../organizations/sponsorships/families-for-enterprise-setup.component";
|
||||||
@@ -172,7 +170,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
HintComponent,
|
HintComponent,
|
||||||
LockComponent,
|
LockComponent,
|
||||||
NavbarComponent,
|
NavbarComponent,
|
||||||
NestedCheckboxComponent,
|
|
||||||
OrganizationSwitcherComponent,
|
OrganizationSwitcherComponent,
|
||||||
OrgAddEditComponent,
|
OrgAddEditComponent,
|
||||||
OrganizationLayoutComponent,
|
OrganizationLayoutComponent,
|
||||||
@@ -194,7 +191,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
OrgReusedPasswordsReportComponent,
|
OrgReusedPasswordsReportComponent,
|
||||||
OrgToolsComponent,
|
OrgToolsComponent,
|
||||||
OrgUnsecuredWebsitesReportComponent,
|
OrgUnsecuredWebsitesReportComponent,
|
||||||
OrgUserAddEditComponent,
|
|
||||||
OrgUserConfirmComponent,
|
OrgUserConfirmComponent,
|
||||||
OrgWeakPasswordsReportComponent,
|
OrgWeakPasswordsReportComponent,
|
||||||
GeneratorComponent,
|
GeneratorComponent,
|
||||||
@@ -292,7 +288,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
HintComponent,
|
HintComponent,
|
||||||
LockComponent,
|
LockComponent,
|
||||||
NavbarComponent,
|
NavbarComponent,
|
||||||
NestedCheckboxComponent,
|
|
||||||
OrganizationSwitcherComponent,
|
OrganizationSwitcherComponent,
|
||||||
OrgAddEditComponent,
|
OrgAddEditComponent,
|
||||||
OrganizationLayoutComponent,
|
OrganizationLayoutComponent,
|
||||||
@@ -314,7 +309,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
OrgReusedPasswordsReportComponent,
|
OrgReusedPasswordsReportComponent,
|
||||||
OrgToolsComponent,
|
OrgToolsComponent,
|
||||||
OrgUnsecuredWebsitesReportComponent,
|
OrgUnsecuredWebsitesReportComponent,
|
||||||
OrgUserAddEditComponent,
|
|
||||||
OrgUserConfirmComponent,
|
OrgUserConfirmComponent,
|
||||||
OrgWeakPasswordsReportComponent,
|
OrgWeakPasswordsReportComponent,
|
||||||
GeneratorComponent,
|
GeneratorComponent,
|
||||||
|
|||||||
@@ -2434,11 +2434,11 @@
|
|||||||
"deleteCollectionConfirmation": {
|
"deleteCollectionConfirmation": {
|
||||||
"message": "Are you sure you want to delete this collection?"
|
"message": "Are you sure you want to delete this collection?"
|
||||||
},
|
},
|
||||||
"editUser": {
|
"editMember": {
|
||||||
"message": "Edit user"
|
"message": "Edit member"
|
||||||
},
|
},
|
||||||
"inviteUser": {
|
"inviteMember": {
|
||||||
"message": "Invite user"
|
"message": "Invite member"
|
||||||
},
|
},
|
||||||
"inviteUserDesc": {
|
"inviteUserDesc": {
|
||||||
"message": "Invite a new user to your organization by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
"message": "Invite a new user to your organization by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div
|
<div
|
||||||
[ngClass]="width"
|
[ngClass]="width"
|
||||||
class="tw-my-4 tw-flex tw-max-h-screen tw-flex-col tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-text-contrast tw-text-main"
|
class="tw-flex tw-flex-col tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-text-contrast tw-text-main"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="tw-flex tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300 tw-p-4"
|
class="tw-flex tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300 tw-p-4"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||||
import { Component, Input } from "@angular/core";
|
import { Component, HostBinding, Input } from "@angular/core";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "bit-dialog",
|
selector: "bit-dialog",
|
||||||
@@ -16,6 +16,8 @@ export class DialogComponent {
|
|||||||
return this._disablePadding;
|
return this._disablePadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostBinding("class") classes = ["tw-flex", "tw-flex-col", "tw-py-4", "tw-max-h-screen"];
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
switch (this.dialogSize) {
|
switch (this.dialogSize) {
|
||||||
case "small": {
|
case "small": {
|
||||||
|
|||||||
Reference in New Issue
Block a user