mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-14 00:26:21 +00:00
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:
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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()
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user