diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 9a68f8bb9fb..24e1bc3ce30 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1656,6 +1656,9 @@ "personalOwnershipPolicyInEffect": { "message": "An organization policy is affecting your ownership options." }, + "personalOwnershipPolicyInEffectImports": { + "message": "An organization policy has blocked importing items into your individual vault." + }, "excludedDomains": { "message": "Excluded domains" }, @@ -2449,6 +2452,114 @@ "message": "Turn off master password re-prompt to edit this field", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, + "importData": { + "message": "Import data", + "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" + }, + "importError": { + "message": "Import error" + }, + "importErrorDesc": { + "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." + }, + "resolveTheErrorsBelowAndTryAgain": { + "message": "Resolve the errors below and try again." + }, + "description": { + "message": "Description" + }, + "importSuccess": { + "message": "Data successfully imported" + }, + "importSuccessNumberOfItems": { + "message": "A total of $AMOUNT$ items were imported.", + "placeholders": { + "amount": { + "content": "$1", + "example": "2" + } + } + }, + "total": { + "message": "Total" + }, + "importWarning": { + "message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "importFormatError": { + "message": "Data is not formatted correctly. Please check your import file and try again." + }, + "importNothingError": { + "message": "Nothing was imported." + }, + "importEncKeyError": { + "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." + }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, + "selectFormat": { + "message": "Select the format of the import file" + }, + "selectImportFile": { + "message": "Select the import file" + }, + "chooseFile": { + "message": "Choose File" + }, + "noFileChosen": { + "message": "No file chosen" + }, + "orCopyPasteFileContents": { + "message": "or copy/paste the import file contents" + }, + "instructionsFor": { + "message": "$NAME$ Instructions", + "description": "The title for the import tool instructions.", + "placeholders": { + "name": { + "content": "$1", + "example": "LastPass (csv)" + } + } + }, + "confirmVaultImport": { + "message": "Confirm vault import" + }, + "confirmVaultImportDesc": { + "message": "This file is password-protected. Please enter the file password to import data." + }, + "confirmFilePassword": { + "message": "Confirm file password" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, diff --git a/apps/browser/src/popup/app-routing.animations.ts b/apps/browser/src/popup/app-routing.animations.ts index 2304944acb0..42baf65c270 100644 --- a/apps/browser/src/popup/app-routing.animations.ts +++ b/apps/browser/src/popup/app-routing.animations.ts @@ -174,6 +174,9 @@ export const routerTransition = trigger("routerTransition", [ transition("clone-cipher => attachments, clone-cipher => collections", inSlideLeft), transition("attachments => clone-cipher, collections => clone-cipher", outSlideRight), + transition("tabs => import", inSlideLeft), + transition("import => tabs", outSlideRight), + transition("tabs => export", inSlideLeft), transition("export => tabs", outSlideRight), diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 10159a715f0..df7b9ffb1cf 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -32,6 +32,7 @@ import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.componen import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; import { SendTypeComponent } from "../tools/popup/send/send-type.component"; import { ExportComponent } from "../tools/popup/settings/export.component"; +import { ImportBrowserComponent } from "../tools/popup/settings/import/import-browser.component"; import { Fido2Component } from "../vault/popup/components/fido2/fido2.component"; import { AddEditComponent } from "../vault/popup/components/vault/add-edit.component"; import { AttachmentsComponent } from "../vault/popup/components/vault/attachments.component"; @@ -222,6 +223,12 @@ const routes: Routes = [ canActivate: [AuthGuard], data: { state: "generator-history" }, }, + { + path: "import", + component: ImportBrowserComponent, + canActivate: [AuthGuard], + data: { state: "import" }, + }, { path: "export", component: ExportComponent, diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss index 3b401d356f5..6cd99abb0d5 100644 --- a/apps/browser/src/popup/scss/base.scss +++ b/apps/browser/src/popup/scss/base.scss @@ -175,7 +175,7 @@ cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb, } } -header { +header:not(bit-callout header) { min-height: 44px; max-height: 44px; display: flex; diff --git a/apps/browser/src/popup/settings/settings.component.ts b/apps/browser/src/popup/settings/settings.component.ts index 274ed871225..252a2097156 100644 --- a/apps/browser/src/popup/settings/settings.component.ts +++ b/apps/browser/src/popup/settings/settings.component.ts @@ -473,8 +473,11 @@ export class SettingsComponent implements OnInit { BrowserApi.createNewTab(url); } - import() { - BrowserApi.createNewTab("https://bitwarden.com/help/import-data/"); + async import() { + await this.router.navigate(["/import"]); + if (await BrowserApi.isPopupOpen()) { + this.popupUtilsService.popOut(window); + } } export() { diff --git a/apps/browser/src/tools/popup/settings/import/import-browser.component.html b/apps/browser/src/tools/popup/settings/import/import-browser.component.html new file mode 100644 index 00000000000..b305e6c395f --- /dev/null +++ b/apps/browser/src/tools/popup/settings/import/import-browser.component.html @@ -0,0 +1,24 @@ +
+
+ +
+

+ {{ "importData" | i18n }} +

+
+ +
+
+
+ +
diff --git a/apps/browser/src/tools/popup/settings/import/import-browser.component.ts b/apps/browser/src/tools/popup/settings/import/import-browser.component.ts new file mode 100644 index 00000000000..3fea3aad04b --- /dev/null +++ b/apps/browser/src/tools/popup/settings/import/import-browser.component.ts @@ -0,0 +1,31 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; +import { Router, RouterLink } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; +import { ImportComponent } from "@bitwarden/importer/ui"; + +@Component({ + templateUrl: "import-browser.component.html", + standalone: true, + imports: [ + CommonModule, + RouterLink, + JslibModule, + DialogModule, + AsyncActionsModule, + ButtonModule, + ImportComponent, + ], +}) +export class ImportBrowserComponent { + protected disabled = false; + protected loading = false; + + constructor(private router: Router) {} + + protected async onSuccessfulImport(organizationId: string): Promise { + this.router.navigate(["/tabs/settings"]); + } +}