Merge pull request #2910 from paperless-ngx/feature-improved-statistics-widget

Feature: Improved statistics widget
This commit is contained in:
shamoon
2023-03-19 23:34:19 -07:00
committed by GitHub
11 changed files with 221 additions and 20 deletions

View File

@@ -2202,20 +2202,48 @@
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="1171694977288479084" datatype="html">
<source>Documents in inbox: <x id="INTERPOLATION" equiv-text="{{statistics?.documents_inbox}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit id="4207135462646354574" datatype="html">
<source>Total documents: <x id="INTERPOLATION" equiv-text="{{statistics?.documents_total}}"/></source>
<trans-unit id="2028517964701399614" datatype="html">
<source>Go to inbox</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="3497361602348932709" datatype="html">
<source>Documents in inbox</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">5</context>
</context-group>
</trans-unit>
<trans-unit id="8809281703097241399" datatype="html">
<source>Go to documents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
</trans-unit>
<trans-unit id="3823413855067727192" datatype="html">
<source>Total documents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">9</context>
</context-group>
</trans-unit>
<trans-unit id="6503529145162789855" datatype="html">
<source>Total characters</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="8693603235657020323" datatype="html">
<source>Other</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts</context>
<context context-type="linenumber">87</context>
</context-group>
</trans-unit>
<trans-unit id="8187573012244728580" datatype="html">
<source>Upload new documents</source>
<context-group purpose="location">

View File

@@ -22,6 +22,7 @@
"@popperjs/core": "^2.11.6",
"bootstrap": "^5.2.3",
"file-saver": "^2.0.5",
"mime-names": "^1.0.0",
"ng2-pdf-viewer": "^9.1.2",
"ngx-color": "^8.0.3",
"ngx-cookie-service": "^15.0.0",
@@ -13766,6 +13767,11 @@
"node": ">= 0.6"
}
},
"node_modules/mime-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mime-names/-/mime-names-1.0.0.tgz",
"integrity": "sha512-vLNEfYU63fz34panv/L3Lh3eW3+v0BlOB+bSGFdntv/gBNnokCbSsaNuHR9vH/NS5oWbL0HqMQf/3we4fRJyIQ=="
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",

View File

@@ -27,6 +27,7 @@
"@popperjs/core": "^2.11.6",
"bootstrap": "^5.2.3",
"file-saver": "^2.0.5",
"mime-names": "^1.0.0",
"ng2-pdf-viewer": "^9.1.2",
"ngx-color": "^8.0.3",
"ngx-cookie-service": "^15.0.0",

View File

@@ -1,6 +1,46 @@
<app-widget-frame title="Statistics" [loading]="loading" i18n-title>
<ng-container content>
<p class="card-text" i18n *ngIf="statistics?.documents_inbox !== null">Documents in inbox: {{statistics?.documents_inbox}}</p>
<p class="card-text" i18n>Total documents: {{statistics?.documents_total}}</p>
<div class="list-group border-light">
<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()" *ngIf="statistics?.documents_inbox !== null" (click)="goToInbox()">
<ng-container i18n>Documents in inbox</ng-container>:
<span class="badge rounded-pill" [class.bg-primary]="statistics?.documents_inbox > 0" [class.bg-muted]="statistics?.documents_inbox === 0">{{statistics?.documents_inbox}}</span>
</a>
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to documents" i18n-title routerLink="/documents/">
<ng-container i18n>Total documents</ng-container>:
<span class="badge bg-primary rounded-pill">{{statistics?.documents_total}}</span>
</a>
<div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
<ng-container i18n>Total characters</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.character_count | number}}</span>
</div>
<div *ngIf="statistics?.document_file_type_counts?.length > 1" class="list-group-item filetypes">
<div class="d-flex justify-content-between align-items-center my-2">
<div class="progress flex-grow-1">
<div *ngFor="let filetype of statistics?.document_file_type_counts; let i = index; let last = last"
class="progress-bar bg-primary text-primary-contrast"
role="progressbar"
[ngbPopover]="getFileTypeName(filetype)"
i18n-ngbPopover
triggers="mouseenter:mouseleave"
[attr.aria-label]="getFileTypeName(filetype)"
[class.me-1px]="!last"
[style.width]="getFileTypePercent(filetype) + '%'"
[style.opacity]="getItemOpacity(i)"
[attr.aria-valuenow]="getFileTypePercent(filetype)"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="d-flex flex-wrap align-items-start">
<div class="d-flex" *ngFor="let filetype of statistics?.document_file_type_counts; let i = index">
<div class="text-nowrap me-2">
<span class="badge rounded-pill bg-primary d-inline-block p-0 me-1" [style.opacity]="getItemOpacity(i)"></span>
<small class="text-nowrap"><span class="fw-bold">{{ getFileTypeExtension(filetype) }}</span>&nbsp;<span class="text-muted">({{getFileTypePercent(filetype) | number: '1.0-1'}}%)</span></small>
</div>
</div>
</div>
</div>
</div>
</ng-container>
</app-widget-frame>

View File

@@ -0,0 +1,10 @@
.filetypes {
.progress {
height: 0.6rem;
}
.badge {
height: 0.6rem;
width: 0.6rem;
}
}

View File

@@ -1,12 +1,23 @@
import { HttpClient } from '@angular/common/http'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Observable, Subscription } from 'rxjs'
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { environment } from 'src/environments/environment'
import * as mimeTypeNames from 'mime-names'
export interface Statistics {
documents_total?: number
documents_inbox?: number
inbox_tag?: number
document_file_type_counts?: DocumentFileType[]
character_count?: number
}
interface DocumentFileType {
mime_type: string
mime_type_count: number
}
@Component({
@@ -19,7 +30,8 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
constructor(
private http: HttpClient,
private consumerStatusService: ConsumerStatusService
private consumerStatusService: ConsumerStatusService,
private documentListViewService: DocumentListViewService
) {}
statistics: Statistics = {}
@@ -34,10 +46,43 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
this.loading = true
this.getStatistics().subscribe((statistics) => {
this.loading = false
const fileTypeMax = 5
if (statistics.document_file_type_counts?.length > fileTypeMax) {
const others = statistics.document_file_type_counts.slice(fileTypeMax)
statistics.document_file_type_counts =
statistics.document_file_type_counts.slice(0, fileTypeMax)
statistics.document_file_type_counts.push({
mime_type: $localize`Other`,
mime_type_count: others.reduce(
(currentValue, documentFileType) =>
documentFileType.mime_type_count + currentValue,
0
),
})
}
this.statistics = statistics
})
}
getFileTypeExtension(filetype: DocumentFileType): string {
return (
mimeTypeNames[filetype.mime_type]?.extensions[0]?.toUpperCase() ??
filetype.mime_type
)
}
getFileTypeName(filetype: DocumentFileType): string {
return mimeTypeNames[filetype.mime_type]?.name ?? filetype.mime_type
}
getFileTypePercent(filetype: DocumentFileType): number {
return (filetype.mime_type_count / this.statistics?.documents_total) * 100
}
getItemOpacity(i: number): number {
return 1 - i / this.statistics?.document_file_type_counts.length
}
ngOnInit(): void {
this.reload()
this.subscription = this.consumerStatusService
@@ -50,4 +95,13 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.subscription.unsubscribe()
}
goToInbox() {
this.documentListViewService.quickFilter([
{
rule_type: FILTER_HAS_TAGS_ALL,
value: this.statistics.inbox_tag.toString(),
},
])
}
}

View File

@@ -629,3 +629,7 @@ code {
.accordion-button::after {
filter: invert(0.5) saturate(0);
}
.me-1px {
margin-right: 1px !important;
}

View File

@@ -231,6 +231,14 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
.dropdown-menu {
--bs-dropdown-color: var(--bs-body-color);
}
.card .list-group-item {
--bs-border-color: rgb(var(--bs-dark-rgb));
.bg-secondary {
background-color: rgb(var(--bs-dark-rgb)) !important;
}
}
}
body.color-scheme-dark {