Enhancement: homepage skeleton screens
This commit is contained in:
parent
8722ff481c
commit
0c469f731d
@ -11,8 +11,19 @@
|
|||||||
>
|
>
|
||||||
@if (savedViewService.loading) {
|
@if (savedViewService.loading) {
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
<div class="card shadow-sm bg-light">
|
||||||
<ng-container i18n>Loading...</ng-container>
|
<div class="card-header">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="ms-n2 me-1">
|
||||||
|
<i-bs name="grip-vertical"></i-bs>
|
||||||
|
</div>
|
||||||
|
<h6 class="card-title mb-0" i18n></h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body"> </div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,9 @@
|
|||||||
<a class="btn-link text-decoration-none" header-buttons [routerLink]="[]" (click)="showAll()" i18n>Show all</a>
|
<a class="btn-link text-decoration-none" header-buttons [routerLink]="[]" (click)="showAll()" i18n>Show all</a>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (documents.length && displayMode === DisplayMode.TABLE) {
|
<div content class="wrapper" [class.reveal]="reveal">
|
||||||
<table content class="table table-hover mb-0 mt-n2 align-middle">
|
@if (displayMode === DisplayMode.TABLE) {
|
||||||
|
<table class="table table-hover mb-0 mt-n2 align-middle">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@for (field of displayFields; track field; let i = $index) {
|
@for (field of displayFields; track field; let i = $index) {
|
||||||
@ -28,10 +29,15 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@for (doc of documents; track doc.id) {
|
@for (doc of documents; track doc.id; let i = $index) {
|
||||||
<tr>
|
<tr>
|
||||||
@for (field of displayFields; track field; let i = $index) {
|
@for (field of displayFields; track field; let j = $index) {
|
||||||
<td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': i > 1 }">
|
<td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': j > 1 }">
|
||||||
|
@if (loading && reveal) {
|
||||||
|
<div class="placeholder-glow text-start">
|
||||||
|
<span class="placeholder bg-secondary w-50" [ngStyle]="{ opacity: 1 - (i * 1/documents.length) }"></span>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
@switch (field) {
|
@switch (field) {
|
||||||
@case (DisplayField.ADDED) {
|
@case (DisplayField.ADDED) {
|
||||||
<a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3" title="Open document" i18n-title>{{doc.added | customDate}}</a>
|
<a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3" title="Open document" i18n-title>{{doc.added | customDate}}</a>
|
||||||
@ -66,7 +72,7 @@
|
|||||||
@if (field.startsWith(DisplayField.CUSTOM_FIELD)) {
|
@if (field.startsWith(DisplayField.CUSTOM_FIELD)) {
|
||||||
<pngx-custom-field-display [document]="doc" [fieldDisplayKey]="field"></pngx-custom-field-display>
|
<pngx-custom-field-display [document]="doc" [fieldDisplayKey]="field"></pngx-custom-field-display>
|
||||||
}
|
}
|
||||||
@if (i === displayFields.length - 1) {
|
@if (j === displayFields.length - 1) {
|
||||||
<div class="btn-group position-absolute top-50 end-0 translate-middle-y" (mouseleave)="popupPreview.close()">
|
<div class="btn-group position-absolute top-50 end-0 translate-middle-y" (mouseleave)="popupPreview.close()">
|
||||||
<pngx-preview-popup [document]="doc" linkClasses="btn px-4 btn-dark border-dark-subtle" #popupPreview>
|
<pngx-preview-popup [document]="doc" linkClasses="btn px-4 btn-dark border-dark-subtle" #popupPreview>
|
||||||
<i-bs width="0.8em" height="0.8em" name="eye"></i-bs>
|
<i-bs width="0.8em" height="0.8em" name="eye"></i-bs>
|
||||||
@ -76,19 +82,21 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
} @else if (documents.length && displayMode === DisplayMode.SMALL_CARDS) {
|
} @else if (displayMode === DisplayMode.SMALL_CARDS) {
|
||||||
<div content class="row row-cols-paperless-cards my-n2">
|
<div class="row row-cols-paperless-cards my-n2">
|
||||||
@for (d of documents; track d.id) {
|
@for (d of documents; track d.id; let i = $index) {
|
||||||
<pngx-document-card-small
|
<pngx-document-card-small
|
||||||
class="p-0"
|
class="p-0"
|
||||||
|
[ngStyle]="{ opacity: !loading && reveal ? 1 : 1 - (i * 1/documents.length) }"
|
||||||
(dblClickDocument)="openDocumentDetail(d)"
|
(dblClickDocument)="openDocumentDetail(d)"
|
||||||
[document]="d"
|
[document]="!loading && reveal ? d : null"
|
||||||
[displayFields]="displayFields"
|
[displayFields]="displayFields"
|
||||||
(clickTag)="clickTag($event)"
|
(clickTag)="clickTag($event)"
|
||||||
(clickCorrespondent)="clickCorrespondent($event)"
|
(clickCorrespondent)="clickCorrespondent($event)"
|
||||||
@ -97,12 +105,13 @@
|
|||||||
</pngx-document-card-small>
|
</pngx-document-card-small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
} @else if (documents.length && displayMode === DisplayMode.LARGE_CARDS) {
|
} @else if (displayMode === DisplayMode.LARGE_CARDS) {
|
||||||
<div content class="row my-n2">
|
<div class="row my-n2">
|
||||||
@for (d of documents; track d.id) {
|
@for (d of documents; track d.id; let i = $index) {
|
||||||
<pngx-document-card-large
|
<pngx-document-card-large
|
||||||
(dblClickDocument)="openDocumentDetail(d)"
|
(dblClickDocument)="openDocumentDetail(d)"
|
||||||
[document]="d"
|
[document]="!loading && reveal ? d : null"
|
||||||
|
[ngStyle]="{ opacity: !loading && reveal ? 1 : 1 - (i * 1/documents.length) }"
|
||||||
[displayFields]="displayFields"
|
[displayFields]="displayFields"
|
||||||
(clickTag)="clickTag($event)"
|
(clickTag)="clickTag($event)"
|
||||||
(clickCorrespondent)="clickCorrespondent($event)"
|
(clickCorrespondent)="clickCorrespondent($event)"
|
||||||
@ -113,8 +122,8 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<p content i18n class="text-center text-muted mb-0 fst-italic">No documents</p>
|
<p i18n class="text-center text-muted mb-0 fst-italic">No documents</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
</pngx-widget-frame>
|
</pngx-widget-frame>
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
.wrapper {
|
||||||
|
transition: all .3s ease-out;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 0;
|
||||||
|
opacity: .1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal {
|
||||||
|
max-height: 1000px;
|
||||||
|
opacity: 1;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
ViewChildren,
|
ViewChildren,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { Subject, takeUntil } from 'rxjs'
|
import { delay, Subject, takeUntil, tap } from 'rxjs'
|
||||||
import {
|
import {
|
||||||
DEFAULT_DASHBOARD_DISPLAY_FIELDS,
|
DEFAULT_DASHBOARD_DISPLAY_FIELDS,
|
||||||
DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
|
DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
|
||||||
@ -52,7 +52,8 @@ export class SavedViewWidgetComponent
|
|||||||
public DisplayField = DisplayField
|
public DisplayField = DisplayField
|
||||||
public CustomFieldDataType = CustomFieldDataType
|
public CustomFieldDataType = CustomFieldDataType
|
||||||
|
|
||||||
loading: boolean = true
|
public loading: boolean = true
|
||||||
|
public reveal: boolean = false
|
||||||
|
|
||||||
private customFields: CustomField[] = []
|
private customFields: CustomField[] = []
|
||||||
|
|
||||||
@ -133,16 +134,22 @@ export class SavedViewWidgetComponent
|
|||||||
this.documentService
|
this.documentService
|
||||||
.listFiltered(
|
.listFiltered(
|
||||||
1,
|
1,
|
||||||
this.savedView.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
|
this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
|
||||||
this.savedView.sort_field,
|
this.savedView.sort_field,
|
||||||
this.savedView.sort_reverse,
|
this.savedView.sort_reverse,
|
||||||
this.savedView.filter_rules,
|
this.savedView.filter_rules,
|
||||||
{ truncate_content: true }
|
{ truncate_content: true }
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(
|
||||||
|
takeUntil(this.unsubscribeNotifier),
|
||||||
|
tap((result) => {
|
||||||
|
this.reveal = true
|
||||||
|
this.documents = result.results
|
||||||
|
}),
|
||||||
|
delay(500)
|
||||||
|
)
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.documents = result.results
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,29 @@
|
|||||||
<pngx-widget-frame title="Statistics" [loading]="loading" i18n-title>
|
<pngx-widget-frame title="Statistics" [loading]="loading" i18n-title>
|
||||||
<ng-container content>
|
<ng-container content>
|
||||||
<div class="list-group border-light">
|
<div class="list-group border-light placeholder-glow">
|
||||||
|
@if (loading) {
|
||||||
|
<div class="list-group-item d-flex">
|
||||||
|
<div class="placeholder w-50"></div>
|
||||||
|
<span class="placeholder badge rounded-pill ms-auto" style="width: 25px;"> </span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex">
|
||||||
|
<div class="placeholder w-25"></div>
|
||||||
|
<span class="placeholder badge rounded-pill ms-auto" style="width: 25px;"> </span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex">
|
||||||
|
<div class="placeholder w-25"></div>
|
||||||
|
<span class="placeholder badge rounded-pill ms-auto" style="width: 25px;"> </span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex">
|
||||||
|
<div class="placeholder w-25"></div>
|
||||||
|
<span class="placeholder badge rounded-pill ms-auto" style="width: 25px;"> </span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item filetypes">
|
||||||
|
<div class="placeholder w-100 d-block mb-2"></div>
|
||||||
|
<div class="placeholder w-100 d-block mb-2"></div>
|
||||||
|
<div class="placeholder w-100 d-block"></div>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
@if (statistics?.documents_inbox !== null) {
|
@if (statistics?.documents_inbox !== null) {
|
||||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void(0)" (click)="goToInbox()">
|
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void(0)" (click)="goToInbox()">
|
||||||
<ng-container i18n>Documents in inbox</ng-container>:
|
<ng-container i18n>Documents in inbox</ng-container>:
|
||||||
@ -21,6 +44,7 @@
|
|||||||
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.current_asn}}</span>
|
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.current_asn}}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@if (statistics?.document_file_type_counts?.length > 1) {
|
@if (statistics?.document_file_type_counts?.length > 1) {
|
||||||
<div class="list-group-item filetypes">
|
<div class="list-group-item filetypes">
|
||||||
<div class="d-flex justify-content-between align-items-center my-2">
|
<div class="d-flex justify-content-between align-items-center my-2">
|
||||||
@ -59,6 +83,11 @@
|
|||||||
|
|
||||||
<div class="list-group border-light mt-3">
|
<div class="list-group border-light mt-3">
|
||||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }">
|
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }">
|
||||||
|
@if (loading) {
|
||||||
|
<div class="placeholder-glow list-group-item">
|
||||||
|
<span class="placeholder w-100"></span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (statistics?.tag_count > 0) {
|
@if (statistics?.tag_count > 0) {
|
||||||
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/tags/">
|
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/tags/">
|
||||||
<ng-container i18n>Tags</ng-container>:
|
<ng-container i18n>Tags</ng-container>:
|
||||||
@ -67,6 +96,11 @@
|
|||||||
}
|
}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
|
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
|
||||||
|
@if (loading) {
|
||||||
|
<div class="placeholder-glow list-group-item">
|
||||||
|
<span class="placeholder w-100"></span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (statistics?.correspondent_count > 0) {
|
@if (statistics?.correspondent_count > 0) {
|
||||||
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/correspondents/">
|
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/correspondents/">
|
||||||
<ng-container i18n>Correspondents</ng-container>:
|
<ng-container i18n>Correspondents</ng-container>:
|
||||||
@ -75,6 +109,11 @@
|
|||||||
}
|
}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
|
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
|
||||||
|
@if (loading) {
|
||||||
|
<div class="placeholder-glow list-group-item">
|
||||||
|
<span class="placeholder w-100"></span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (statistics?.document_type_count > 0) {
|
@if (statistics?.document_type_count > 0) {
|
||||||
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documenttypes/">
|
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documenttypes/">
|
||||||
<ng-container i18n>Document Types</ng-container>:
|
<ng-container i18n>Document Types</ng-container>:
|
||||||
@ -83,6 +122,11 @@
|
|||||||
}
|
}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
|
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
|
||||||
|
@if (loading) {
|
||||||
|
<div class="placeholder-glow list-group-item">
|
||||||
|
<span class="placeholder w-100"></span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (statistics?.storage_path_count > 0) {
|
@if (statistics?.storage_path_count > 0) {
|
||||||
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/storagepaths/">
|
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/storagepaths/">
|
||||||
<ng-container i18n>Storage Paths</ng-container>:
|
<ng-container i18n>Storage Paths</ng-container>:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="card shadow-sm bg-light" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
|
<div class="card shadow-sm bg-light fade" [class.reveal]="reveal" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
i-bs {
|
i-bs {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@ -35,6 +35,7 @@ describe('WidgetFrameComponent', () => {
|
|||||||
|
|
||||||
fixture = TestBed.createComponent(WidgetFrameComponent)
|
fixture = TestBed.createComponent(WidgetFrameComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
|
jest.useFakeTimers()
|
||||||
|
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
})
|
})
|
||||||
@ -51,4 +52,10 @@ describe('WidgetFrameComponent', () => {
|
|||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(fixture.debugElement.query(By.css('.spinner-border'))).not.toBeNull()
|
expect(fixture.debugElement.query(By.css('.spinner-border'))).not.toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should reveal', () => {
|
||||||
|
expect(component.reveal).toBeFalsy()
|
||||||
|
jest.advanceTimersByTime(100)
|
||||||
|
expect(component.reveal).toBeTruthy()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { AfterViewInit, Component, Input } from '@angular/core'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-widget-frame',
|
selector: 'pngx-widget-frame',
|
||||||
templateUrl: './widget-frame.component.html',
|
templateUrl: './widget-frame.component.html',
|
||||||
styleUrls: ['./widget-frame.component.scss'],
|
styleUrls: ['./widget-frame.component.scss'],
|
||||||
})
|
})
|
||||||
export class WidgetFrameComponent {
|
export class WidgetFrameComponent implements AfterViewInit {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@ -16,4 +16,12 @@ export class WidgetFrameComponent {
|
|||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
draggable: any
|
draggable: any
|
||||||
|
|
||||||
|
public reveal: boolean = false
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.reveal = true
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<div class="card mb-3 shadow-sm bg-light" [class.card-selected]="selected" [class.document-card]="selectable" (mouseleave)="mouseLeaveCard()">
|
<div class="card document-card-large mb-3 shadow-sm bg-light placeholder-glow" [class.card-selected]="selected" [class.document-card]="selectable" (mouseleave)="mouseLeaveCard()">
|
||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="col-md-2 doc-img-container rounded-start" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit()">
|
<div class="col-md-2 doc-img-container rounded-start" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit()">
|
||||||
|
@if (document) {
|
||||||
<img [src]="getThumbUrl()" class="card-img doc-img border-end rounded-start" [class.inverted]="getIsThumbInverted()">
|
<img [src]="getThumbUrl()" class="card-img doc-img border-end rounded-start" [class.inverted]="getIsThumbInverted()">
|
||||||
|
|
||||||
<div class="border-end border-bottom bg-light document-card-check">
|
<div class="border-end border-bottom bg-light document-card-check">
|
||||||
@ -9,12 +10,16 @@
|
|||||||
<label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
|
<label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder bg-secondary w-100 card-img doc-img border-end rounded-start"></div>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<h5 class="card-title">
|
<h5 class="card-title w-100">
|
||||||
|
@if (document) {
|
||||||
@if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
|
@if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
|
||||||
@if (clickCorrespondent.observers.length ) {
|
@if (clickCorrespondent.observers.length ) {
|
||||||
<a title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name}}</a>
|
<a title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name}}</a>
|
||||||
@ -31,9 +36,13 @@
|
|||||||
<pngx-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle class="ms-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></pngx-tag>
|
<pngx-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle class="ms-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></pngx-tag>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder bg-secondary w-75 mb-3"> </div>
|
||||||
|
}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
|
@if (document) {
|
||||||
@if (document.__search_hit__ && document.__search_hit__.highlights) {
|
@if (document.__search_hit__ && document.__search_hit__.highlights) {
|
||||||
<span [innerHtml]="document.__search_hit__.highlights"></span>
|
<span [innerHtml]="document.__search_hit__.highlights"></span>
|
||||||
}
|
}
|
||||||
@ -46,10 +55,16 @@
|
|||||||
@if (!document.__search_hit__?.score) {
|
@if (!document.__search_hit__?.score) {
|
||||||
<span class="result-content">{{contentTrimmed}}</span>
|
<span class="result-content">{{contentTrimmed}}</span>
|
||||||
}
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder bg-secondary w-100 d-block mb-2"></div>
|
||||||
|
<div class="placeholder bg-secondary w-75 d-block mb-2"></div>
|
||||||
|
<div class="placeholder bg-secondary w-25 d-block"></div>
|
||||||
|
}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="d-flex flex-column flex-md-row align-items-md-center">
|
<div class="d-flex flex-column flex-md-row align-items-md-center">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
|
@if (document) {
|
||||||
<a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()">
|
<a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()">
|
||||||
<i-bs name="diagram-3"></i-bs> <span class="d-none d-md-inline" i18n>More like this</span>
|
<i-bs name="diagram-3"></i-bs> <span class="d-none d-md-inline" i18n>More like this</span>
|
||||||
</a>
|
</a>
|
||||||
@ -62,9 +77,16 @@
|
|||||||
<a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
|
<a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
|
||||||
<i-bs name="download"></i-bs> <span class="d-none d-md-inline" i18n>Download</span>
|
<i-bs name="download"></i-bs> <span class="d-none d-md-inline" i18n>Download</span>
|
||||||
</a>
|
</a>
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;"> </div>
|
||||||
|
<div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;"> </div>
|
||||||
|
<div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;"> </div>
|
||||||
|
<div class="placeholder btn btn-sm btn-outline-secondary bg-secondary" style="width: 60px;"> </div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-group list-group-horizontal border-0 card-info ms-md-auto mt-2 mt-md-0">
|
<div class="list-group list-group-horizontal border-0 card-info ms-md-auto mt-2 mt-md-0">
|
||||||
|
@if (document) {
|
||||||
@if (displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
|
@if (displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
|
||||||
<button routerLink="/documents/{{document.id}}/notes" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="View notes" i18n-title>
|
<button routerLink="/documents/{{document.id}}/notes" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="View notes" i18n-title>
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="chat-left-text"></i-bs><small>{{document.notes.length}} Notes</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="chat-left-text"></i-bs><small>{{document.notes.length}} Notes</small>
|
||||||
@ -138,9 +160,16 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder list-group-item bg-secondary w-25"> </div>
|
||||||
|
<div class="placeholder list-group-item bg-secondary w-25"> </div>
|
||||||
|
<div class="placeholder list-group-item bg-secondary w-25"> </div>
|
||||||
|
<div class="placeholder list-group-item bg-secondary w-25"> </div>
|
||||||
|
<div class="placeholder list-group-item bg-secondary w-25"> </div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<div class="col p-2 h-100">
|
<div class="col p-2 h-100">
|
||||||
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected" (mouseleave)="mouseLeaveCard()">
|
<div class="card h-100 shadow-sm document-card" [class.placeholder-glow]="!document" [class.card-selected]="selected" (mouseleave)="mouseLeaveCard()">
|
||||||
<div class="border-bottom doc-img-container rounded-top" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit(this)">
|
<div class="border-bottom doc-img-container rounded-top" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit(this)">
|
||||||
|
@if (document) {
|
||||||
<img class="card-img doc-img" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
|
<img class="card-img doc-img" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
|
||||||
|
|
||||||
<div class="border-end border-bottom bg-light py-1 px-2 document-card-check">
|
<div class="border-end border-bottom bg-light py-1 px-2 document-card-check">
|
||||||
@ -9,8 +10,11 @@
|
|||||||
<label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
|
<label class="form-check-label" for="smallCardCheck{{document.id}}"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder bg-secondary w-100 card-img doc-img"></div>
|
||||||
|
}
|
||||||
|
|
||||||
@if (displayFields?.includes(DisplayField.TAGS)) {
|
@if (document && displayFields?.includes(DisplayField.TAGS)) {
|
||||||
<div class="tags d-flex flex-column text-end position-absolute me-1 fs-6">
|
<div class="tags d-flex flex-column text-end position-absolute me-1 fs-6">
|
||||||
@for (t of getTagsLimited$() | async; track t) {
|
@for (t of getTagsLimited$() | async; track t) {
|
||||||
<pngx-tag [tag]="t" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="true" linkTitle="Toggle tag filter" i18n-linkTitle></pngx-tag>
|
<pngx-tag [tag]="t" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="true" linkTitle="Toggle tag filter" i18n-linkTitle></pngx-tag>
|
||||||
@ -24,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
|
@if (document && displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) {
|
||||||
<a routerLink="/documents/{{document.id}}/notes" class="document-card-notes py-2 px-1">
|
<a routerLink="/documents/{{document.id}}/notes" class="document-card-notes py-2 px-1">
|
||||||
<span class="badge rounded-pill bg-light border text-primary">
|
<span class="badge rounded-pill bg-light border text-primary">
|
||||||
<i-bs width="1.2em" height="1.2em" class="ms-1 me-1" name="chat-left-text"></i-bs>
|
<i-bs width="1.2em" height="1.2em" class="ms-1 me-1" name="chat-left-text"></i-bs>
|
||||||
@ -34,6 +38,7 @@
|
|||||||
|
|
||||||
<div class="card-body bg-light p-2">
|
<div class="card-body bg-light p-2">
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
|
@if (document) {
|
||||||
@if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
|
@if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
|
||||||
<a title="Toggle correspondent filter" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name ?? privateName}}</a>
|
<a title="Toggle correspondent filter" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name ?? privateName}}</a>
|
||||||
@if (displayFields.includes(DisplayField.TITLE)) {:}
|
@if (displayFields.includes(DisplayField.TITLE)) {:}
|
||||||
@ -41,10 +46,15 @@
|
|||||||
@if (displayFields.includes(DisplayField.TITLE)) {
|
@if (displayFields.includes(DisplayField.TITLE)) {
|
||||||
{{document.title | documentTitle}}
|
{{document.title | documentTitle}}
|
||||||
}
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder bg-secondary w-100"></div>
|
||||||
|
<div class="placeholder bg-secondary w-50"></div>
|
||||||
|
}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer pt-0 pb-2 px-2">
|
<div class="card-footer pt-0 pb-2 px-2">
|
||||||
<div class="list-group list-group-flush border-0 pt-1 pb-2 card-info">
|
<div class="list-group list-group-flush border-0 pt-1 pb-2 card-info">
|
||||||
|
@if (document) {
|
||||||
@if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) {
|
@if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) {
|
||||||
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle document type filter" i18n-title
|
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle document type filter" i18n-title
|
||||||
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
||||||
@ -123,9 +133,13 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="placeholder bg-secondary w-100"></div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div class="btn-group w-100">
|
<div class="btn-group w-100">
|
||||||
|
@if (document) {
|
||||||
<a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n-title>
|
<a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n-title>
|
||||||
<i-bs name="file-earmark-richtext"></i-bs>
|
<i-bs name="file-earmark-richtext"></i-bs>
|
||||||
</a>
|
</a>
|
||||||
@ -135,6 +149,11 @@
|
|||||||
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" i18n-title (click)="$event.stopPropagation()">
|
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" i18n-title (click)="$event.stopPropagation()">
|
||||||
<i-bs name="download"></i-bs>
|
<i-bs name="download"></i-bs>
|
||||||
</a>
|
</a>
|
||||||
|
} @else {
|
||||||
|
<button class="btn btn-sm btn-outline-secondary placeholder bg-secondary"></button>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary placeholder bg-secondary"></button>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary placeholder bg-secondary"></button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -203,8 +203,11 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
|
|||||||
|
|
||||||
@supports (hanging-punctuation: first) and (font: -apple-system-body) and (-webkit-appearance: none) {
|
@supports (hanging-punctuation: first) and (font: -apple-system-body) and (-webkit-appearance: none) {
|
||||||
// Safari does not like the filters on the image, see https://github.com/paperless-ngx/paperless-ngx/pull/8121
|
// Safari does not like the filters on the image, see https://github.com/paperless-ngx/paperless-ngx/pull/8121
|
||||||
|
.document-card:not(.placeholder-glow),
|
||||||
|
.document-card-large:not(.placeholder-glow) {
|
||||||
.doc-img-container {
|
.doc-img-container {
|
||||||
background-color: #ffffff !important;
|
background-color: #ffffff !important;
|
||||||
|
width: 101%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.doc-img {
|
.doc-img {
|
||||||
@ -218,6 +221,7 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
|
|||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.paperless-input-select .ng-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option:not(.ng-option-selected):hover,
|
.paperless-input-select .ng-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option:not(.ng-option-selected):hover,
|
||||||
.paperless-input-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
|
.paperless-input-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user