mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 08:43:33 +00:00
[PM-4222] Make importer UI reusable (#6504)
* Split up import/export into separate modules * Fix routing and apply PR feedback * Renamed OrganizationExport exports to OrganizationVaultExport * Make import dialogs standalone and move them to libs/importer * Make import.component re-usable - Move functionality which was previously present on the org-import.component into import.component - Move import.component into libs/importer Make import.component standalone Create import-web.component to represent Web UI Fix module imports and routing Remove unused org-import-files * Renamed filenames according to export rename * Make ImportWebComponent standalone, simplify routing * Pass organizationId as Input to ImportComponent * use formLoading and formDisabled outputs * Emit an event when the import succeeds Remove Angular router from base-component as other clients might not have routing (i.e. desktop) Move logic that happened on web successful import into the import-web.component * fix table themes on desktop & browser * fix fileSelector button styles * update selectors to use tools prefix; remove unused selectors * Wall off UI components in libs/importer Create barrel-file for libs/importer/components Remove components and dialog exports from libs/importer/index.ts Extend libs/shared/tsconfig.libs.json to include @bitwarden/importer/ui -> libs/importer/components Extend apps/web/tsconfig.ts to include @bitwarden/importer/ui Update all usages * Rename @bitwarden/importer to @bitwarden/importer/core Create more barrel files in libs/importer/* Update imports within libs/importer Extend tsconfig files Update imports in web, desktop, browser and cli * Lazy-load the ImportWebComponent via both routes * Use SharedModule as import in import-web.component * File selector should be displayed as secondary * Use bitSubmit to override submit preventDefault (#6607) Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> Co-authored-by: William Martin <contact@willmartian.com>
This commit is contained in:
committed by
GitHub
parent
d0e72f5554
commit
9e290a3fed
@@ -0,0 +1,32 @@
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle>
|
||||
{{ "confirmVaultImport" | i18n }}
|
||||
</span>
|
||||
|
||||
<div bitDialogContent>
|
||||
{{ "confirmVaultImportDesc" | i18n }}
|
||||
<bit-form-field class="tw-mt-6">
|
||||
<bit-label>{{ "confirmFilePassword" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="password"
|
||||
name="filePassword"
|
||||
formControlName="filePassword"
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<button type="button" bitSuffix bitIconButton bitPasswordInputToggle></button>
|
||||
</bit-form-field>
|
||||
</div>
|
||||
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton buttonType="primary" type="submit">
|
||||
<span>{{ "importData" | i18n }}</span>
|
||||
</button>
|
||||
<button bitButton bitDialogClose buttonType="secondary" type="button">
|
||||
<span>{{ "cancel" | i18n }}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import {
|
||||
AsyncActionsModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
FormFieldModule,
|
||||
IconButtonModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
templateUrl: "file-password-prompt.component.html",
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
JslibModule,
|
||||
DialogModule,
|
||||
FormFieldModule,
|
||||
AsyncActionsModule,
|
||||
ButtonModule,
|
||||
IconButtonModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
})
|
||||
export class FilePasswordPromptComponent {
|
||||
formGroup = this.formBuilder.group({
|
||||
filePassword: ["", Validators.required],
|
||||
});
|
||||
|
||||
constructor(public dialogRef: DialogRef, protected formBuilder: FormBuilder) {}
|
||||
|
||||
submit = () => {
|
||||
this.formGroup.markAsTouched();
|
||||
if (!this.formGroup.valid) {
|
||||
return;
|
||||
}
|
||||
this.dialogRef.close(this.formGroup.value.filePassword);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle>
|
||||
{{ "importError" | i18n }}
|
||||
</span>
|
||||
|
||||
<span bitDialogContent>
|
||||
<div>{{ "resolveTheErrorsBelowAndTryAgain" | i18n }}</div>
|
||||
<bit-table [dataSource]="dataSource">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "name" | i18n }}</th>
|
||||
<th bitCell>{{ "description" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *ngFor="let r of rows$ | async">
|
||||
<td bitCell>{{ r.type }}</td>
|
||||
<td bitCell>{{ r.message }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</span>
|
||||
|
||||
<div bitDialogFooter>
|
||||
<button bitButton bitDialogClose buttonType="primary" type="button">
|
||||
{{ "ok" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</bit-dialog>
|
||||
@@ -0,0 +1,36 @@
|
||||
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Inject, OnInit } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ButtonModule, DialogModule, TableDataSource, TableModule } from "@bitwarden/components";
|
||||
|
||||
export interface ErrorListItem {
|
||||
type: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: "./import-error-dialog.component.html",
|
||||
standalone: true,
|
||||
imports: [CommonModule, JslibModule, DialogModule, TableModule, ButtonModule],
|
||||
})
|
||||
export class ImportErrorDialogComponent implements OnInit {
|
||||
protected dataSource = new TableDataSource<ErrorListItem>();
|
||||
|
||||
constructor(public dialogRef: DialogRef, @Inject(DIALOG_DATA) public data: Error) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const split = this.data.message.split("\n\n");
|
||||
if (split.length == 1) {
|
||||
this.dataSource.data = [{ type: "", message: this.data.message }];
|
||||
return;
|
||||
}
|
||||
|
||||
const data: ErrorListItem[] = [];
|
||||
split.forEach((line) => {
|
||||
data.push({ type: "", message: line });
|
||||
});
|
||||
this.dataSource.data = data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle>
|
||||
{{ "importSuccess" | i18n }}
|
||||
</span>
|
||||
|
||||
<div bitDialogContent>
|
||||
<span>{{ "importSuccessNumberOfItems" | i18n : this.data.ciphers.length }}</span>
|
||||
<bit-table [dataSource]="dataSource">
|
||||
<ng-container header>
|
||||
<tr>
|
||||
<th bitCell>{{ "type" | i18n }}</th>
|
||||
<th bitCell>{{ "total" | i18n }}</th>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *ngFor="let r of rows$ | async">
|
||||
<td bitCell>
|
||||
<i class="bwi bwi-fw bwi-{{ r.icon }}" aria-hidden="true"></i>
|
||||
{{ r.type | i18n }}
|
||||
</td>
|
||||
<td bitCell>{{ r.count }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</div>
|
||||
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitDialogClose buttonType="primary" type="button">
|
||||
{{ "ok" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
@@ -0,0 +1,82 @@
|
||||
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Inject, OnInit } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { ButtonModule, DialogModule, TableDataSource, TableModule } from "@bitwarden/components";
|
||||
|
||||
import { ImportResult } from "../../models";
|
||||
|
||||
export interface ResultList {
|
||||
icon: string;
|
||||
type: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: "./import-success-dialog.component.html",
|
||||
standalone: true,
|
||||
imports: [CommonModule, JslibModule, DialogModule, TableModule, ButtonModule],
|
||||
})
|
||||
export class ImportSuccessDialogComponent implements OnInit {
|
||||
protected dataSource = new TableDataSource<ResultList>();
|
||||
|
||||
constructor(public dialogRef: DialogRef, @Inject(DIALOG_DATA) public data: ImportResult) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data != null) {
|
||||
this.dataSource.data = this.buildResultList();
|
||||
}
|
||||
}
|
||||
|
||||
private buildResultList(): ResultList[] {
|
||||
let logins = 0;
|
||||
let cards = 0;
|
||||
let identities = 0;
|
||||
let secureNotes = 0;
|
||||
this.data.ciphers.map((c) => {
|
||||
switch (c.type) {
|
||||
case CipherType.Login:
|
||||
logins++;
|
||||
break;
|
||||
case CipherType.Card:
|
||||
cards++;
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
secureNotes++;
|
||||
break;
|
||||
case CipherType.Identity:
|
||||
identities++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const list: ResultList[] = [];
|
||||
if (logins > 0) {
|
||||
list.push({ icon: "globe", type: "typeLogin", count: logins });
|
||||
}
|
||||
if (cards > 0) {
|
||||
list.push({ icon: "credit-card", type: "typeCard", count: cards });
|
||||
}
|
||||
if (identities > 0) {
|
||||
list.push({ icon: "id-card", type: "typeIdentity", count: identities });
|
||||
}
|
||||
if (secureNotes > 0) {
|
||||
list.push({ icon: "sticky-note", type: "typeSecureNote", count: secureNotes });
|
||||
}
|
||||
if (this.data.folders.length > 0) {
|
||||
list.push({ icon: "folder", type: "folders", count: this.data.folders.length });
|
||||
}
|
||||
if (this.data.collections.length > 0) {
|
||||
list.push({
|
||||
icon: "collection",
|
||||
type: "collections",
|
||||
count: this.data.collections.length,
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
3
libs/importer/src/components/dialog/index.ts
Normal file
3
libs/importer/src/components/dialog/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./import-error-dialog.component";
|
||||
export * from "./import-success-dialog.component";
|
||||
export * from "./file-password-prompt.component";
|
||||
Reference in New Issue
Block a user