From a8c6c55e3bd79f64d91528367616a1332d5b5075 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:52:47 -0700 Subject: [PATCH] Useful buttons for saved view widgets --- src-ui/messages.xlf | 165 ++++++++++-------- .../saved-view-widget.component.html | 25 ++- .../saved-view-widget.component.scss | 12 ++ .../saved-view-widget.component.spec.ts | 41 ++++- .../saved-view-widget.component.ts | 56 +++++- .../document-card-large.component.html | 2 +- .../document-card-small.component.html | 2 +- .../popover-preview/popover-preview.scss | 4 +- src-ui/src/styles.scss | 5 + src-ui/src/theme.scss | 1 + 10 files changed, 228 insertions(+), 85 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 56bce7c38..8296b6e4e 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -723,7 +723,7 @@ src/app/components/manage/settings/settings.component.ts - 597 + 600 @@ -2013,6 +2013,10 @@ src/app/components/common/input/permissions/permissions-form/permissions-form.component.html 46 + + src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html + 17 + src/app/components/document-list/document-card-large/document-card-large.component.html 49 @@ -2303,6 +2307,36 @@ 20 + + View Preview + + src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html + 19 + + + + Download + + src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html + 29 + + + src/app/components/document-detail/document-detail.component.html + 19 + + + src/app/components/document-list/bulk-editor/bulk-editor.component.html + 102 + + + src/app/components/document-list/document-card-large/document-card-large.component.html + 64 + + + src/app/components/document-list/document-card-small/document-card-small.component.html + 99 + + Statistics @@ -2482,25 +2516,6 @@ 5,6 - - Download - - src/app/components/document-detail/document-detail.component.html - 19 - - - src/app/components/document-list/bulk-editor/bulk-editor.component.html - 102 - - - src/app/components/document-list/document-card-large/document-card-large.component.html - 64 - - - src/app/components/document-list/document-card-small/document-card-small.component.html - 99 - - Download original @@ -2898,19 +2913,19 @@ src/app/components/manage/settings/settings.component.ts - 708 + 711 src/app/components/manage/settings/settings.component.ts - 768 + 771 src/app/components/manage/settings/settings.component.ts - 835 + 838 src/app/components/manage/settings/settings.component.ts - 898 + 901 @@ -2925,19 +2940,19 @@ src/app/components/manage/settings/settings.component.ts - 710 + 713 src/app/components/manage/settings/settings.component.ts - 770 + 773 src/app/components/manage/settings/settings.component.ts - 837 + 840 src/app/components/manage/settings/settings.component.ts - 900 + 903 @@ -4478,231 +4493,231 @@ Saved view "" deleted. src/app/components/manage/settings/settings.component.ts - 479 + 482 Settings saved src/app/components/manage/settings/settings.component.ts - 581 + 584 Settings were saved successfully. src/app/components/manage/settings/settings.component.ts - 582 + 585 Settings were saved successfully. Reload is required to apply some changes. src/app/components/manage/settings/settings.component.ts - 586 + 589 Reload now src/app/components/manage/settings/settings.component.ts - 587 + 590 Use system language src/app/components/manage/settings/settings.component.ts - 606 + 609 Use date format of display language src/app/components/manage/settings/settings.component.ts - 613 + 616 Error while storing settings on server. src/app/components/manage/settings/settings.component.ts - 633 + 636 Password has been changed, you will be logged out momentarily. src/app/components/manage/settings/settings.component.ts - 676 + 679 Saved user "". src/app/components/manage/settings/settings.component.ts - 683 + 686 Error saving user. src/app/components/manage/settings/settings.component.ts - 695 + 698 Confirm delete user account src/app/components/manage/settings/settings.component.ts - 706 + 709 This operation will permanently delete this user account. src/app/components/manage/settings/settings.component.ts - 707 + 710 Deleted user src/app/components/manage/settings/settings.component.ts - 716 + 719 Error deleting user. src/app/components/manage/settings/settings.component.ts - 724 + 727 Saved group "". src/app/components/manage/settings/settings.component.ts - 745 + 748 Error saving group. src/app/components/manage/settings/settings.component.ts - 755 + 758 Confirm delete user group src/app/components/manage/settings/settings.component.ts - 766 + 769 This operation will permanently delete this user group. src/app/components/manage/settings/settings.component.ts - 767 + 770 Deleted group src/app/components/manage/settings/settings.component.ts - 776 + 779 Error deleting group. src/app/components/manage/settings/settings.component.ts - 784 + 787 Saved account "". src/app/components/manage/settings/settings.component.ts - 810 + 813 Error saving account. src/app/components/manage/settings/settings.component.ts - 822 + 825 Confirm delete mail account src/app/components/manage/settings/settings.component.ts - 833 + 836 This operation will permanently delete this mail account. src/app/components/manage/settings/settings.component.ts - 834 + 837 Deleted mail account src/app/components/manage/settings/settings.component.ts - 843 + 846 Error deleting mail account. src/app/components/manage/settings/settings.component.ts - 852 + 855 Saved rule "". src/app/components/manage/settings/settings.component.ts - 873 + 876 Error saving rule. src/app/components/manage/settings/settings.component.ts - 885 + 888 Confirm delete mail rule src/app/components/manage/settings/settings.component.ts - 896 + 899 This operation will permanently delete this mail rule. src/app/components/manage/settings/settings.component.ts - 897 + 900 Deleted mail rule src/app/components/manage/settings/settings.component.ts - 906 + 909 Error deleting mail rule. src/app/components/manage/settings/settings.component.ts - 915 + 918 @@ -5090,28 +5105,28 @@ Document already exists. src/app/services/consumer-status.service.ts - 15 + 16 Document with ASN already exists. src/app/services/consumer-status.service.ts - 16 + 17 File not found. src/app/services/consumer-status.service.ts - 17 + 18 Pre-consume script does not exist. src/app/services/consumer-status.service.ts - 18 + 19 Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -5119,7 +5134,7 @@ Error while executing pre-consume script. src/app/services/consumer-status.service.ts - 19 + 20 Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -5127,7 +5142,7 @@ Post-consume script does not exist. src/app/services/consumer-status.service.ts - 20 + 21 Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -5135,7 +5150,7 @@ Error while executing post-consume script. src/app/services/consumer-status.service.ts - 21 + 22 Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -5143,49 +5158,49 @@ Received new file. src/app/services/consumer-status.service.ts - 22 + 23 File type not supported. src/app/services/consumer-status.service.ts - 23 + 24 Processing document... src/app/services/consumer-status.service.ts - 24 + 25 Generating thumbnail... src/app/services/consumer-status.service.ts - 25 + 26 Retrieving date from document... src/app/services/consumer-status.service.ts - 26 + 27 Saving document... src/app/services/consumer-status.service.ts - 27 + 28 Finished. src/app/services/consumer-status.service.ts - 28 + 29 diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html index ededd7c52..e12f91a72 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html @@ -6,14 +6,33 @@ - Created + Created Title - + {{doc.created_date | customDate}} - {{doc.title | documentTitle}} + + {{doc.title | documentTitle}} + + + + + + + + + + + + + + + + diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss index 62bea686f..941d474d4 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss @@ -10,3 +10,15 @@ th:first-child { tbody app-tag { cursor: pointer; } + +tr .btn-group { + margin-right: 2px; + box-shadow: -6px 0px 4px -1px rgba(var(--bs-body-bg-rgb), .5); + opacity: 0; + pointer-events: none; +} + +tr:hover .btn-group { + opacity: 1; + pointer-events: all; +} diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts index 27ba45fae..7e1a77ae8 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts @@ -1,6 +1,11 @@ import { DatePipe } from '@angular/common' import { HttpClientTestingModule } from '@angular/common/http/testing' -import { ComponentFixture, TestBed } from '@angular/core/testing' +import { + ComponentFixture, + TestBed, + fakeAsync, + tick, +} from '@angular/core/testing' import { Router } from '@angular/router' import { RouterTestingModule } from '@angular/router/testing' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' @@ -21,6 +26,8 @@ import { PermissionsService } from 'src/app/services/permissions.service' import { DocumentService } from 'src/app/services/rest/document.service' import { WidgetFrameComponent } from '../widget-frame/widget-frame.component' import { SavedViewWidgetComponent } from './saved-view-widget.component' +import { By } from '@angular/platform-browser' +import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe' const savedView: PaperlessSavedView = { id: 1, @@ -64,6 +71,7 @@ describe('SavedViewWidgetComponent', () => { IfPermissionsDirective, CustomDatePipe, DocumentTitlePipe, + SafeUrlPipe, ], providers: [ PermissionsGuard, @@ -107,8 +115,39 @@ describe('SavedViewWidgetComponent', () => { fixture.detectChanges() expect(fixture.debugElement.nativeElement.textContent).toContain('doc2') expect(fixture.debugElement.nativeElement.textContent).toContain('doc3') + // preview + download buttons + expect( + fixture.debugElement.queryAll(By.css('td a.btn'))[0].attributes['href'] + ).toEqual(component.getPreviewUrl(documentResults[0])) + expect( + fixture.debugElement.queryAll(By.css('td a.btn'))[1].attributes['href'] + ).toEqual(component.getDownloadUrl(documentResults[0])) }) + it('should show preview on mouseover after delay to preload content', fakeAsync(() => { + jest.spyOn(documentService, 'listFiltered').mockReturnValue( + of({ + all: [2, 3], + count: 2, + results: documentResults, + }) + ) + component.ngOnInit() + fixture.detectChanges() + component.mouseEnterPreview(documentResults[0]) + expect(component.popover.isOpen()).toBeTruthy() + expect(component.popoverHidden).toBeTruthy() + tick(600) + expect(component.popoverHidden).toBeFalsy() + component.mouseLeaveCard() + + component.mouseEnterPreview(documentResults[1]) + tick(100) + component.mouseLeavePreview() + tick(600) + expect(component.popover.isOpen()).toBeFalsy() + })) + it('should call api endpoint and load results', () => { const listAllSpy = jest.spyOn(documentService, 'listFiltered') listAllSpy.mockReturnValue( diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 97c311da3..5264acda8 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -1,4 +1,12 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core' +import { + Component, + Input, + OnDestroy, + OnInit, + QueryList, + ViewChild, + ViewChildren, +} from '@angular/core' import { Router } from '@angular/router' import { Subscription } from 'rxjs' import { PaperlessDocument } from 'src/app/data/paperless-document' @@ -10,11 +18,15 @@ import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type' import { OpenDocumentsService } from 'src/app/services/open-documents.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component' +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap' @Component({ selector: 'app-saved-view-widget', templateUrl: './saved-view-widget.component.html', - styleUrls: ['./saved-view-widget.component.scss'], + styleUrls: [ + './saved-view-widget.component.scss', + '../../../document-list/popover-preview/popover-preview.scss', + ], }) export class SavedViewWidgetComponent extends ComponentWithPermissions @@ -39,6 +51,12 @@ export class SavedViewWidgetComponent subscription: Subscription + @ViewChildren('popover') popovers: QueryList + popover: NgbPopover + + mouseOnPreview = false + popoverHidden = true + ngOnInit(): void { this.reload() this.subscription = this.consumerStatusService @@ -87,4 +105,38 @@ export class SavedViewWidgetComponent { rule_type: FILTER_HAS_TAGS_ALL, value: tag.id.toString() }, ]) } + + getPreviewUrl(document: PaperlessDocument): string { + return this.documentService.getPreviewUrl(document.id) + } + + getDownloadUrl(document: PaperlessDocument): string { + return this.documentService.getDownloadUrl(document.id) + } + + mouseEnterPreview(doc: PaperlessDocument) { + this.popover = this.popovers.get(this.documents.indexOf(doc)) + this.mouseOnPreview = true + if (!this.popover.isOpen()) { + // we're going to open but hide to pre-load content during hover delay + this.popover.open() + this.popoverHidden = true + setTimeout(() => { + if (this.mouseOnPreview) { + // show popover + this.popoverHidden = false + } else { + this.popover.close() + } + }, 600) + } + } + + mouseLeavePreview() { + this.mouseOnPreview = false + } + + mouseLeaveCard() { + this.popover?.close() + } } diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index d0cf4002e..0dca65318 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -50,7 +50,7 @@ + autoClose="true" popoverClass="shadow popover-preview" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover"> View diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index a4024d70d..d1b8aff95 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -87,7 +87,7 @@ + autoClose="true" popoverClass="shadow popover-preview" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover"> diff --git a/src-ui/src/app/components/document-list/popover-preview/popover-preview.scss b/src-ui/src/app/components/document-list/popover-preview/popover-preview.scss index 8d31bf2fb..b51e2e66b 100644 --- a/src-ui/src/app/components/document-list/popover-preview/popover-preview.scss +++ b/src-ui/src/app/components/document-list/popover-preview/popover-preview.scss @@ -1,4 +1,4 @@ -::ng-deep app-document-list .popover { +::ng-deep .popover.popover-preview { max-width: 40rem; .preview { @@ -16,7 +16,7 @@ } } - ::ng-deep .popover-hidden .popover { +::ng-deep .popover-hidden .popover { opacity: 0; pointer-events: none; } diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index 824d3b278..1bdd84190 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -433,6 +433,11 @@ ul.pagination { height: 1em; } +.buttonicon-xs { + width: 0.8em; + height: 0.8em; +} + .sidebaricon { width: 16px; height: 16px; diff --git a/src-ui/src/theme.scss b/src-ui/src/theme.scss index e3a90238f..ddf635e41 100644 --- a/src-ui/src/theme.scss +++ b/src-ui/src/theme.scss @@ -71,6 +71,7 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,