Enhancement: dashboard improvements, drag-n-drop reorder dashboard views (#4252)

* Updated dashboard

* Make entire screen dropzone on dashboard too

* Floating upload widget status alerts

* Visual tweaks: spacing, borders

* Better empty view widget

* Support drag + drop reorder of dashboard saved views

* Update messages.xlf

* Disable dashbaord dnd if global dnd active

* Remove ngx-file-drop dep, rebuild file-drop & upload files widget

* Revert custom file drop implementation

* Try patch-package fix

* Simplify dropzone transitions to make more reliable

* Update messages.xlf

* Update dashboard.spec.ts

* Fix coverage
This commit is contained in:
shamoon
2023-09-28 10:18:12 -07:00
committed by GitHub
parent 96176589ca
commit 6973691cce
45 changed files with 1715 additions and 534 deletions

View File

@@ -1,22 +1,38 @@
<pngx-widget-frame [title]="savedView.name" [loading]="loading">
<pngx-widget-frame
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"
[title]="savedView.name"
[loading]="loading"
[draggable]="savedView"
(dndStart)="dndStart.emit($event)"
(dndMoved)="dndMoved.emit($event)"
(dndCanceled)="dndCanceled.emit($event)"
(dndEnd)="dndEnd.emit($event)"
>
<a class="btn-link" header-buttons [routerLink]="[]" (click)="showAll()" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }" i18n>Show all</a>
<a *ngIf="documents.length" class="btn-link" header-buttons [routerLink]="[]" (click)="showAll()" i18n>Show all</a>
<table content class="table table-sm table-hover table-borderless mb-0">
<table *ngIf="documents.length; else empty" content class="table table-hover mb-0 align-middle">
<thead>
<tr>
<th scope="col" i18n>Created</th>
<th scope="col" i18n>Title</th>
<th scope="col" class="d-none d-md-table-cell" i18n>Tags</th>
<th scope="col" class="d-none d-md-table-cell" i18n>Correspondent</th>
</tr>
</thead>
<tbody *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<tbody>
<tr *ngFor="let doc of documents" (mouseleave)="mouseLeaveCard()">
<td><a routerLink="/documents/{{doc.id}}" class="d-block text-dark text-decoration-none">{{doc.created_date | customDate}}</a></td>
<td class="position-relative">
<a routerLink="/documents/{{doc.id}}" title="Edit" i18n-title class="d-block text-dark text-decoration-none">{{doc.title | documentTitle}}<pngx-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ms-1" (click)="clickTag(t, $event)"></pngx-tag></a>
<td class="py-2 py-md-3"><a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none">{{doc.created_date | customDate}}</a></td>
<td class="py-2 py-md-3">
<a routerLink="/documents/{{doc.id}}" title="Edit" i18n-title class="btn-link text-dark text-decoration-none">{{doc.title | documentTitle}}</a>
</td>
<td class="py-2 py-md-3 d-none d-md-table-cell">
<pngx-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ms-1" (click)="clickTag(t, $event)"></pngx-tag>
</td>
<td class="position-relative py-2 py-md-3 d-none d-md-table-cell">
<a *ngIf="doc.correspondent !== null" class="btn-link" routerLink="/documents" [queryParams]="getCorrespondentQueryParams(doc.correspondent)">{{(doc.correspondent$ | async)?.name}}</a>
<div class="btn-group position-absolute top-50 end-0 translate-middle-y">
<a [href]="getPreviewUrl(doc)" title="View Preview" i18n-title target="_blank" class="btn btn-sm px-4 py-0 btn-dark border-dark-subtle"
<a [href]="getPreviewUrl(doc)" title="View Preview" i18n-title target="_blank" class="btn px-4 btn-dark border-dark-subtle"
[ngbPopover]="previewContent" [popoverTitle]="doc.title | documentTitle"
autoClose="true" popoverClass="shadow popover-preview" container="body" (mouseenter)="mouseEnterPreview(doc)" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
<svg class="buttonicon-xs" fill="currentColor">
@@ -26,7 +42,7 @@
<ng-template #previewContent>
<object [data]="getPreviewUrl(doc) | safeUrl" class="preview" width="100%"></object>
</ng-template>
<a [href]="getDownloadUrl(doc)" class="btn btn-sm px-4 py-0 btn-dark border-dark-subtle" title="Download" i18n-title (click)="$event.stopPropagation()">
<a [href]="getDownloadUrl(doc)" class="btn px-4 btn-dark border-dark-subtle" title="Download" i18n-title (click)="$event.stopPropagation()">
<svg class="buttonicon-xs" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#download"/>
</svg>
@@ -37,4 +53,8 @@
</tbody>
</table>
<ng-template #empty>
<p i18n class="text-center text-muted mb-0 fst-italic">No documents</p>
</ng-template>
</pngx-widget-frame>

View File

@@ -5,9 +5,12 @@ table {
th:first-child {
width: 25%;
@media (min-width: 768px) {
width: 15%;
}
}
tbody app-tag {
tbody pngx-tag {
cursor: pointer;
}
@@ -22,3 +25,8 @@ tr:hover .btn-group {
opacity: 1;
pointer-events: all;
}
td.py-3 {
padding-top: 0.75em !important;
padding-bottom: 0.75em !important;
}

View File

@@ -28,6 +28,7 @@ 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'
import { DndModule } from 'ngx-drag-drop'
const savedView: PaperlessSavedView = {
id: 1,
@@ -52,6 +53,7 @@ const documentResults = [
{
id: 3,
title: 'doc3',
correspondent: 0,
},
]
@@ -89,6 +91,7 @@ describe('SavedViewWidgetComponent', () => {
HttpClientTestingModule,
NgbModule,
RouterTestingModule.withRoutes(routes),
DndModule,
],
}).compileComponents()

View File

@@ -1,23 +1,29 @@
import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
QueryList,
ViewChildren,
} from '@angular/core'
import { Router } from '@angular/router'
import { Params, Router } from '@angular/router'
import { Subject, takeUntil } from 'rxjs'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentService } from 'src/app/services/rest/document.service'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
import {
FILTER_CORRESPONDENT,
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'
import { queryParamsFromFilterRules } from 'src/app/utils/query-params'
@Component({
selector: 'pngx-saved-view-widget',
@@ -38,7 +44,8 @@ export class SavedViewWidgetComponent
private router: Router,
private list: DocumentListViewService,
private consumerStatusService: ConsumerStatusService,
public openDocumentsService: OpenDocumentsService
public openDocumentsService: OpenDocumentsService,
public documentListViewService: DocumentListViewService
) {
super()
}
@@ -46,6 +53,18 @@ export class SavedViewWidgetComponent
@Input()
savedView: PaperlessSavedView
@Output()
dndStart: EventEmitter<DragEvent> = new EventEmitter()
@Output()
dndMoved: EventEmitter<DragEvent> = new EventEmitter()
@Output()
dndCanceled: EventEmitter<DragEvent> = new EventEmitter()
@Output()
dndEnd: EventEmitter<DragEvent> = new EventEmitter()
documents: PaperlessDocument[] = []
unsubscribeNotifier: Subject<any> = new Subject()
@@ -141,4 +160,15 @@ export class SavedViewWidgetComponent
mouseLeaveCard() {
this.popover?.close()
}
getCorrespondentQueryParams(correspondentId: number): Params {
return correspondentId !== undefined
? queryParamsFromFilterRules([
{
rule_type: FILTER_CORRESPONDENT,
value: correspondentId.toString(),
},
])
: null
}
}