From e90ba197d8af0138e37b56d041b5abbd90294973 Mon Sep 17 00:00:00 2001 From: aasandei-vsp Date: Thu, 11 Sep 2025 20:58:28 +0300 Subject: [PATCH] [PER-10275] Fixed archive selector modal available archives list --- .../component-library/components.module.ts | 3 + .../basic-loading-spinner.component.html | 85 +++++++ .../basic-loading-spinner.component.scss | 29 +++ .../basic-loading-spinner.component.spec.ts | 22 ++ .../basic-loading-spinner.component.ts | 9 + .../button/button.component.spec.ts | 4 +- .../archive-switcher.component.spec.ts | 66 ------ src/app/core/core.module.ts | 2 - src/app/core/core.routes.ts | 2 +- .../archive-switcher.component.html | 5 +- .../archive-switcher.component.scss | 0 .../archive-switcher.component.spec.ts | 218 ++++++++++++++++++ .../archive-switcher.component.ts | 20 +- .../services/account/account.service.ts | 2 +- src/app/shared/shared.module.ts | 5 + 15 files changed, 391 insertions(+), 81 deletions(-) create mode 100644 src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.html create mode 100644 src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.scss create mode 100644 src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.spec.ts create mode 100644 src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.ts delete mode 100644 src/app/core/components/archive-switcher/archive-switcher.component.spec.ts rename src/app/{core => shared}/components/archive-switcher/archive-switcher.component.html (82%) rename src/app/{core => shared}/components/archive-switcher/archive-switcher.component.scss (100%) create mode 100644 src/app/shared/components/archive-switcher/archive-switcher.component.spec.ts rename src/app/{core => shared}/components/archive-switcher/archive-switcher.component.ts (91%) diff --git a/src/app/component-library/components.module.ts b/src/app/component-library/components.module.ts index adcef52df..1c4a27911 100644 --- a/src/app/component-library/components.module.ts +++ b/src/app/component-library/components.module.ts @@ -11,6 +11,7 @@ import { ButtonComponent } from './components/button/button.component'; import { ToggleComponent } from './components/toggle/toggle.component'; import { CheckboxComponent } from './components/checkbox/checkbox.component'; import { LoadingSpinnerComponent } from './components/loading-spinner/loading-spinner.component'; +import { BasicLoadingSpinnerComponent } from './components/basic-loading-spinner/basic-loading-spinner.component'; @NgModule({ declarations: [ @@ -19,6 +20,7 @@ import { LoadingSpinnerComponent } from './components/loading-spinner/loading-sp ButtonComponent, CheckboxComponent, LoadingSpinnerComponent, + BasicLoadingSpinnerComponent, ], imports: [CommonModule, FontAwesomeModule, FormsModule, ReactiveFormsModule], exports: [ @@ -27,6 +29,7 @@ import { LoadingSpinnerComponent } from './components/loading-spinner/loading-sp ButtonComponent, CheckboxComponent, LoadingSpinnerComponent, + BasicLoadingSpinnerComponent, ], }) export class ComponentsModule { diff --git a/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.html b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.html new file mode 100644 index 000000000..9c1266923 --- /dev/null +++ b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.html @@ -0,0 +1,85 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.scss b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.scss new file mode 100644 index 000000000..d3eb851c7 --- /dev/null +++ b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.scss @@ -0,0 +1,29 @@ +.outer-circle { + animation: spin-outer 2s linear infinite; + transform-origin: 50% 50%; /* Center the rotation */ + transform-box: fill-box; +} + +.inner-circle { + animation: spin-inner 2s linear infinite; + transform-origin: 50% 50%; /* Center the rotation */ + transform-box: fill-box; +} + +@keyframes spin-outer { + 0% { + transform: scale(1.4) rotate(300deg); + } + 100% { + transform: scale(1.4) rotate(660deg); + } +} + +@keyframes spin-inner { + 0% { + transform: scale(0.9) translate(-0.5px, 6.5px) rotate(45deg); + } + 100% { + transform: scale(0.9) translate(-0.5px, 6px) rotate(-315deg); + } +} diff --git a/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.spec.ts b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.spec.ts new file mode 100644 index 000000000..a5b614905 --- /dev/null +++ b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BasicLoadingSpinnerComponent } from './basic-loading-spinner.component'; + +describe('BasicLoadingSpinnerComponent', () => { + let component: BasicLoadingSpinnerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BasicLoadingSpinnerComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BasicLoadingSpinnerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.ts b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.ts new file mode 100644 index 000000000..d058ba1b7 --- /dev/null +++ b/src/app/component-library/components/basic-loading-spinner/basic-loading-spinner.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'pr-basic-loading-spinner', + templateUrl: './basic-loading-spinner.component.html', + styleUrl: './basic-loading-spinner.component.scss', + standalone: false, +}) +export class BasicLoadingSpinnerComponent {} diff --git a/src/app/component-library/components/button/button.component.spec.ts b/src/app/component-library/components/button/button.component.spec.ts index 6e376238e..2ebb8810c 100644 --- a/src/app/component-library/components/button/button.component.spec.ts +++ b/src/app/component-library/components/button/button.component.spec.ts @@ -1,12 +1,12 @@ import { Shallow } from 'shallow-render'; -import { SharedModule } from '@shared/shared.module'; +import { ComponentsModule } from '../../components.module'; import { ButtonComponent } from './button.component'; describe('ButtonComponent', () => { let shallow: Shallow; beforeEach(async () => { - shallow = new Shallow(ButtonComponent, SharedModule); + shallow = new Shallow(ButtonComponent, ComponentsModule); }); it('should create', async () => { diff --git a/src/app/core/components/archive-switcher/archive-switcher.component.spec.ts b/src/app/core/components/archive-switcher/archive-switcher.component.spec.ts deleted file mode 100644 index d47691d17..000000000 --- a/src/app/core/components/archive-switcher/archive-switcher.component.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import * as Testing from '@root/test/testbedConfig'; -import { cloneDeep } from 'lodash'; - -import { ArchiveResponse } from '@shared/services/api/index.repo'; -import { AccountService } from '@shared/services/account/account.service'; -import { SharedModule } from '@shared/shared.module'; -import { ArchiveVO } from '@root/app/models'; -import { ActivatedRoute } from '@angular/router'; -import { ArchiveSwitcherComponent } from './archive-switcher.component'; - -const archiveResponseData = require('@root/test/responses/archive.get.multiple.success.json'); - -describe('ArchiveSwitcherComponent', () => { - let component: ArchiveSwitcherComponent; - let fixture: ComponentFixture; - - const archiveResponse = new ArchiveResponse(archiveResponseData); - const archives = archiveResponse.getArchiveVOs(); - const currentArchive = new ArchiveVO(archives.pop()); - - beforeEach(async () => { - const config = cloneDeep(Testing.BASE_TEST_CONFIG); - - config.imports.push(SharedModule); - config.declarations.push(ArchiveSwitcherComponent); - - config.providers.push({ - provide: ActivatedRoute, - useValue: { - snapshot: { - data: { - archives: archives, - }, - }, - }, - }); - TestBed.configureTestingModule(config).compileComponents(); - - const accountService = TestBed.inject(AccountService) as AccountService; - accountService.setArchive(currentArchive); - - fixture = TestBed.createComponent(ArchiveSwitcherComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - afterEach(() => { - const accountService = TestBed.inject(AccountService) as AccountService; - accountService.clearArchive(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should have the correct number of archives listed', () => { - expect(component.archives.length).toEqual(archives.length); - - const element = fixture.debugElement.nativeElement as HTMLElement; - - expect( - element.querySelectorAll('.archive-list pr-archive-small').length, - ).toEqual(component.archives.length); - }); -}); diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index e667de11f..e1dbd3404 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -29,7 +29,6 @@ import { ManageMetadataModule } from '../archive-settings/manage-metadata/manage import { DirectiveModule } from '../directive/directive.module'; import { FilesystemModule } from '../filesystem/filesystem.module'; import { UserChecklistModule } from '../user-checklist/user-checklist.module'; -import { ArchiveSwitcherComponent } from './components/archive-switcher/archive-switcher.component'; import { MultiSelectStatusComponent } from './components/multi-select-status/multi-select-status.component'; import { EditService } from './services/edit/edit.service'; import { AccountSettingsComponent } from './components/account-settings/account-settings.component'; @@ -90,7 +89,6 @@ import { RedeemGiftComponent } from './components/redeem-gift/redeem-gift.compon RightMenuComponent, UploadProgressComponent, UploadButtonComponent, - ArchiveSwitcherComponent, FolderPickerComponent, MultiSelectStatusComponent, AccountSettingsComponent, diff --git a/src/app/core/core.routes.ts b/src/app/core/core.routes.ts index 47de9a6fd..6a5bbc6db 100644 --- a/src/app/core/core.routes.ts +++ b/src/app/core/core.routes.ts @@ -7,7 +7,7 @@ import { RootFolderResolveService } from '@core/resolves/root-folder-resolve.ser import { RecordResolveService } from '@core/resolves/record-resolve.service'; import { ArchivesResolveService } from '@core/resolves/archives-resolve.service'; import { SharedModule } from '@shared/shared.module'; -import { ArchiveSwitcherComponent } from '@core/components/archive-switcher/archive-switcher.component'; +import { ArchiveSwitcherComponent } from '@shared/components/archive-switcher/archive-switcher.component'; import { GlobalSearchResultsComponent } from '@search/components/global-search-results/global-search-results.component'; import { RoutedDialogWrapperComponent } from '@shared/components/routed-dialog-wrapper/routed-dialog-wrapper.component'; import { RoutesWithData } from '../app.routes'; diff --git a/src/app/core/components/archive-switcher/archive-switcher.component.html b/src/app/shared/components/archive-switcher/archive-switcher.component.html similarity index 82% rename from src/app/core/components/archive-switcher/archive-switcher.component.html rename to src/app/shared/components/archive-switcher/archive-switcher.component.html index 97177a187..d801df57f 100644 --- a/src/app/core/components/archive-switcher/archive-switcher.component.html +++ b/src/app/shared/components/archive-switcher/archive-switcher.component.html @@ -9,9 +9,12 @@ > } - @if (!archives.length) { + @if (!archivesLoading && !archives.length) {
You have no other archives
} + @if (archivesLoading) { + + }
diff --git a/src/app/core/components/archive-switcher/archive-switcher.component.scss b/src/app/shared/components/archive-switcher/archive-switcher.component.scss similarity index 100% rename from src/app/core/components/archive-switcher/archive-switcher.component.scss rename to src/app/shared/components/archive-switcher/archive-switcher.component.scss diff --git a/src/app/shared/components/archive-switcher/archive-switcher.component.spec.ts b/src/app/shared/components/archive-switcher/archive-switcher.component.spec.ts new file mode 100644 index 000000000..30f2fb676 --- /dev/null +++ b/src/app/shared/components/archive-switcher/archive-switcher.component.spec.ts @@ -0,0 +1,218 @@ +import { + ComponentFixture, + TestBed, + fakeAsync, + tick, +} from '@angular/core/testing'; +import { AccountService } from '@shared/services/account/account.service'; +import { ApiService } from '@shared/services/api/api.service'; +import { DataService } from '@shared/services/data/data.service'; +import { PrConstantsService } from '@shared/services/pr-constants/pr-constants.service'; +import { PromptService } from '@shared/services/prompt/prompt.service'; +import { MessageService } from '@shared/services/message/message.service'; +import { Router } from '@angular/router'; +import { ArchiveVO, FolderVO } from '@root/app/models'; +import { Component } from '@angular/core'; +import { ArchiveSwitcherComponent } from './archive-switcher.component'; + +@Component({ + selector: 'pr-archive-small', + template: '', + standalone: true, // ✅ This makes it standalone +}) +class MockArchiveSmallComponent {} + +@Component({ + selector: 'pr-basic-loading-spinner', + template: '', + standalone: true, +}) +class MockLoadingSpinnerComponent {} + +describe('ArchiveSwitcherComponent', () => { + let component: ArchiveSwitcherComponent; + let fixture: ComponentFixture; + + const mockAccountService = { + getArchive: jasmine + .createSpy() + .and.returnValue( + new ArchiveVO({ archiveId: '123', fullName: 'Current Archive' }), + ), + refreshArchives: jasmine.createSpy().and.returnValue( + Promise.resolve([ + { archiveId: '123', fullName: 'Current Archive' }, + { archiveId: '456', fullName: 'Other Archive' }, + ]), + ), + setArchive: jasmine.createSpy(), + changeArchive: jasmine.createSpy().and.returnValue(Promise.resolve()), + }; + + const mockApiService = { + archive: { + accept: jasmine.createSpy().and.returnValue(Promise.resolve()), + create: jasmine.createSpy().and.returnValue( + Promise.resolve({ + getArchiveVO: () => + new ArchiveVO({ archiveId: '789', fullName: 'New Archive' }), + }), + ), + }, + }; + + const mockDataService = { + setCurrentFolder: jasmine.createSpy(), + }; + + const mockPromptService = { + promptButtons: jasmine + .createSpy() + .and.returnValue(Promise.resolve('switch')), + prompt: jasmine.createSpy().and.returnValue( + Promise.resolve({ + fullName: 'New Archive', + type: 'type.archive.person', + }), + ), + }; + + const mockMessageService = { + showError: jasmine.createSpy(), + }; + + const mockRouter = { + navigate: jasmine.createSpy(), + }; + + const mockPrConstants = { + translate: jasmine.createSpy().and.returnValue('Translated Role'), + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ArchiveSwitcherComponent], + imports: [MockArchiveSmallComponent, MockLoadingSpinnerComponent], + providers: [ + { provide: AccountService, useValue: mockAccountService }, + { provide: ApiService, useValue: mockApiService }, + { provide: DataService, useValue: mockDataService }, + { provide: PrConstantsService, useValue: mockPrConstants }, + { provide: PromptService, useValue: mockPromptService }, + { provide: MessageService, useValue: mockMessageService }, + { provide: Router, useValue: mockRouter }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ArchiveSwitcherComponent); + component = fixture.componentInstance; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize and load archives', fakeAsync(() => { + component.ngOnInit(); + tick(); + + expect(mockDataService.setCurrentFolder).toHaveBeenCalledWith( + jasmine.any(FolderVO), + ); + + expect(mockAccountService.refreshArchives).toHaveBeenCalled(); + expect(component.archives.length).toBe(1); + expect(component.archivesLoading).toBeFalse(); + })); + + it('should trigger animation in ngAfterViewInit', () => { + const mockNodeList: NodeListOf = { + length: 1, + item: () => document.createElement('div'), + 0: document.createElement('div'), + forEach: (callback) => + callback(document.createElement('div'), 0, mockNodeList), + } as unknown as NodeListOf; + + spyOn(document, 'querySelectorAll').and.returnValue(mockNodeList); + component.ngAfterViewInit(); + + expect(document.querySelectorAll).toHaveBeenCalledWith( + '.archive-list pr-archive-small', + ); + }); + + it('should handle archiveClick and switch archive', fakeAsync(() => { + const archive = new ArchiveVO({ + archiveId: '456', + fullName: 'Other Archive', + }); + spyOn(archive, 'isPending').and.returnValue(false); + + component.archiveClick(archive); + tick(); + + expect(mockPromptService.promptButtons).toHaveBeenCalled(); + expect(mockAccountService.changeArchive).toHaveBeenCalledWith(archive); + expect(mockRouter.navigate).toHaveBeenCalledWith(['/private']); + })); + + it('should handle createArchiveClick and push new archive', fakeAsync(() => { + spyOn(component, 'archiveClick'); + component.createArchiveClick(); + tick(); + + expect(mockPromptService.prompt).toHaveBeenCalled(); + expect(mockApiService.archive.create).toHaveBeenCalled(); + expect(component.archives.length).toBe(1); + expect(component.archiveClick).toHaveBeenCalled(); + })); + + it('should show empty message when no archives and not loading', () => { + component.archives = []; + component.archivesLoading = false; + fixture.detectChanges(); + + const emptyMessage = fixture.nativeElement.querySelector('.archives-empty'); + + expect(emptyMessage).toBeTruthy(); + expect(emptyMessage.textContent).toContain('You have no other archives'); + }); + + it('should show loading spinner when archivesLoading is true', () => { + component.archivesLoading = true; + fixture.detectChanges(); + + const spinner = fixture.nativeElement.querySelector( + 'pr-basic-loading-spinner', + ); + + expect(spinner).toBeTruthy(); + }); + + it('should render archive items when archives are present', fakeAsync(() => { + component.archives = [ + new ArchiveVO({ archiveId: '1', fullName: 'Archive One' }), + new ArchiveVO({ archiveId: '2', fullName: 'Archive Two' }), + ]; + component.archivesLoading = false; + fixture.detectChanges(); + + const archiveItems = + fixture.nativeElement.querySelectorAll('pr-archive-small'); + + expect(archiveItems.length).toBe(2); + })); + + it('should call createArchiveClick on button click', () => { + spyOn(component, 'createArchiveClick'); + fixture.detectChanges(); + + const buttons = fixture.nativeElement.querySelectorAll( + '.archive-list-button button', + ); + buttons[0].click(); + + expect(component.createArchiveClick).toHaveBeenCalled(); + }); +}); diff --git a/src/app/core/components/archive-switcher/archive-switcher.component.ts b/src/app/shared/components/archive-switcher/archive-switcher.component.ts similarity index 91% rename from src/app/core/components/archive-switcher/archive-switcher.component.ts rename to src/app/shared/components/archive-switcher/archive-switcher.component.ts index 8fad95fa1..b6f20b117 100644 --- a/src/app/core/components/archive-switcher/archive-switcher.component.ts +++ b/src/app/shared/components/archive-switcher/archive-switcher.component.ts @@ -1,5 +1,4 @@ -import { Component, AfterViewInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, AfterViewInit, OnInit } from '@angular/core'; import { Validators } from '@angular/forms'; import { remove, orderBy } from 'lodash'; @@ -21,6 +20,7 @@ import { ArchiveResponse } from '@shared/services/api/index.repo'; import { PrConstantsService } from '@shared/services/pr-constants/pr-constants.service'; import { RELATIONSHIP_FIELD } from '@shared/components/prompt/prompt-fields'; import { DataService } from '@shared/services/data/data.service'; +import { Router } from '@angular/router'; @Component({ selector: 'pr-archive-switcher', @@ -28,19 +28,22 @@ import { DataService } from '@shared/services/data/data.service'; styleUrls: ['./archive-switcher.component.scss'], standalone: false, }) -export class ArchiveSwitcherComponent implements AfterViewInit { +export class ArchiveSwitcherComponent implements OnInit, AfterViewInit { public currentArchive: ArchiveVO; - public archives: ArchiveVO[]; + public archives: ArchiveVO[] = []; + public archivesLoading = true; + constructor( private accountService: AccountService, private api: ApiService, private data: DataService, private prConstants: PrConstantsService, - private route: ActivatedRoute, private prompt: PromptService, private message: MessageService, private router: Router, - ) { + ) {} + + async ngOnInit() { this.data.setCurrentFolder( new FolderVO({ displayName: 'Archives', @@ -48,9 +51,9 @@ export class ArchiveSwitcherComponent implements AfterViewInit { type: 'page', }), ); - this.currentArchive = accountService.getArchive(); + this.currentArchive = this.accountService.getArchive(); - const archivesData = this.route.snapshot.data.archives || []; + const archivesData = await this.accountService.refreshArchives(); const archives = orderBy( archivesData.map((archiveData) => new ArchiveVO(archiveData)), 'fullName', @@ -63,6 +66,7 @@ export class ArchiveSwitcherComponent implements AfterViewInit { this.accountService.setArchive(this.currentArchive); this.archives = archives as ArchiveVO[]; + this.archivesLoading = false; } ngAfterViewInit() { diff --git a/src/app/shared/services/account/account.service.ts b/src/app/shared/services/account/account.service.ts index 2d101554c..56b4920aa 100644 --- a/src/app/shared/services/account/account.service.ts +++ b/src/app/shared/services/account/account.service.ts @@ -18,7 +18,7 @@ import { AccessRoleType, checkMinimumAccess, } from '@models/access-role'; -import { ArchiveSwitcherComponent } from '@core/components/archive-switcher/archive-switcher.component'; +import { ArchiveSwitcherComponent } from '@shared/components/archive-switcher/archive-switcher.component'; import { DialogCdkService } from '@root/app/dialog-cdk/dialog-cdk.service'; import * as Sentry from '@sentry/browser'; import { HttpV2Service } from '../http-v2/http-v2.service'; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 4f92b7c2e..122207f2a 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -24,6 +24,7 @@ import { import { faFileArchive, faPenSquare } from '@fortawesome/free-solid-svg-icons'; import OpenSeadragon from 'openseadragon'; import { DialogCdkService } from '../dialog-cdk/dialog-cdk.service'; +import { ComponentsModule } from '../component-library/components.module'; import { ArchivePickerComponent } from './components/archive-picker/archive-picker.component'; import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; import { FileSizePipe } from './pipes/filesize.pipe'; @@ -68,6 +69,7 @@ import { AccessRolePipe } from './pipes/access-role.pipe'; import { MobileBannerComponent } from './components/mobile-banner/mobile-banner.component'; import { ZoomingImageViewerComponent } from './components/zooming-image-viewer/zooming-image-viewer.component'; import { GetFileTypePipe } from './pipes/get-file-type.pipe'; +import { ArchiveSwitcherComponent } from './components/archive-switcher/archive-switcher.component'; @NgModule({ imports: [ @@ -81,6 +83,7 @@ import { GetFileTypePipe } from './pipes/get-file-type.pipe'; NgbDropdownModule, NgbPaginationModule, FontAwesomeModule, + ComponentsModule, ], exports: [ CommonModule, @@ -91,6 +94,7 @@ import { GetFileTypePipe } from './pipes/get-file-type.pipe'; BgImageSrcDirective, ArchiveSmallComponent, ArchivePickerComponent, + ArchiveSwitcherComponent, TimelineCompleteDialogComponent, BreadcrumbComponent, BreadcrumbsComponent, @@ -146,6 +150,7 @@ import { GetFileTypePipe } from './pipes/get-file-type.pipe'; PromptComponent, ArchiveSmallComponent, ArchivePickerComponent, + ArchiveSwitcherComponent, BreadcrumbComponent, BreadcrumbsComponent, FileSizePipe,