mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-19 10:19:27 -05:00
Merge pull request #452 from shamoon/feature/consumer-alerts
Updates to consumer status alerts
This commit is contained in:
commit
24313fe001
@ -1,25 +1,47 @@
|
|||||||
<app-widget-frame title="Upload new documents" i18n-title>
|
<app-widget-frame title="Upload new documents" i18n-title [class.has-multiple-status]="(getStatus().length + getStatusesHidden().length)> 1">
|
||||||
|
<div header-buttons>
|
||||||
|
<button type="button" class="btn btn-link dismiss-all" [disabled]="!getStatus().length" (click)="dismissAll()">
|
||||||
|
<small class="mr-1">Hide all</small>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" fill="currentColor" class="bi bi-check2-all" viewBox="0 0 16 16">
|
||||||
|
<path d="M12.354 4.354a.5.5 0 0 0-.708-.708L5 10.293 1.854 7.146a.5.5 0 1 0-.708.708l3.5 3.5a.5.5 0 0 0 .708 0l7-7zm-4.208 7l-.896-.897.707-.707.543.543 6.646-6.647a.5.5 0 0 1 .708.708l-7 7a.5.5 0 0 1-.708 0z"/>
|
||||||
|
<path d="M5.354 7.146l.896.897-.707.707-.897-.896a.5.5 0 1 1 .708-.708z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div content>
|
<div content>
|
||||||
<form>
|
<form>
|
||||||
<ngx-file-drop dropZoneLabel="Drop documents here or" browseBtnLabel="Browse files" (onFileDrop)="dropped($event)"
|
<ngx-file-drop dropZoneLabel="Drop documents here or" browseBtnLabel="Browse files" (onFileDrop)="dropped($event)"
|
||||||
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)" dropZoneClassName="bg-light card"
|
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)" dropZoneClassName="bg-light card"
|
||||||
multiple="true" contentClassName="justify-content-center d-flex align-items-center p-5" [showBrowseBtn]=true
|
multiple="true" contentClassName="justify-content-center d-flex align-items-center p-5" [showBrowseBtn]=true
|
||||||
browseBtnClassName="btn btn-sm btn-outline-primary ml-2" i18n-dropZoneLabel i18n-browseBtnLabel>
|
browseBtnClassName="btn btn-sm btn-outline-primary ml-2" i18n-dropZoneLabel i18n-browseBtnLabel>
|
||||||
|
|
||||||
</ngx-file-drop>
|
</ngx-file-drop>
|
||||||
</form>
|
</form>
|
||||||
<p>Uploading {{getStatusUploading().length}} files...</p>
|
|
||||||
<div *ngFor="let status of getStatus()">
|
<div *ngFor="let status of getStatus()">
|
||||||
<p>{{status.filename}}: {{status.message}}</p>
|
<ng-container [ngTemplateOutlet]="consumerAlert" [ngTemplateOutletContext]="{ $implicit: status }"></ng-container>
|
||||||
<ngb-progressbar [value]="status.getProgress()" [max]="1" [striped]="true" [animated]="!isFinished(status)" [type]="getType(status)">
|
</div>
|
||||||
</ngb-progressbar>
|
<div *ngIf="getStatusesHidden().length" class="alerts-hidden">
|
||||||
|
<p *ngIf="!alertsExpanded" class="mt-3 mb-0 text-center">{{getStatusesHidden().length}} more hidden <button class="btn btn-sm btn-link py-0" (click)="alertsExpanded = !alertsExpanded" aria-controls="hiddenAlerts" [attr.aria-expanded]="alertsExpanded">Show all</button></p>
|
||||||
<div *ngIf="isFinished(status)" class="mb-2">
|
<div #hiddenAlerts="ngbCollapse" [(ngbCollapse)]="!alertsExpanded">
|
||||||
<button *ngIf="status.documentId" class="btn btn-sm btn-outline-primary mr-2" routerLink="/documents/{{status.documentId}}" (click)="dismiss(status)">Open document</button>
|
<div *ngFor="let status of getStatusesHidden()">
|
||||||
<button class="btn btn-sm btn-outline-secondary" (click)="dismiss(status)">Dismiss</button>
|
<ng-container [ngTemplateOutlet]="consumerAlert" [ngTemplateOutletContext]="{ $implicit: status }"></ng-container>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</app-widget-frame>
|
</app-widget-frame>
|
||||||
|
|
||||||
|
<ng-template #consumerAlert let-status>
|
||||||
|
<ngb-alert type="secondary" class="mt-2 mb-0" [dismissible]="isFinished(status)" (closed)="dismiss(status)">
|
||||||
|
<h6 class="alert-heading">{{status.filename}}</h6>
|
||||||
|
<p class="mb-0 pb-1" *ngIf="!isFinished(status) || (isFinished(status) && !status.documentId)">{{status.message}}</p>
|
||||||
|
<ngb-progressbar [value]="status.getProgress()" [max]="1" [type]="getStatusColor(status)"></ngb-progressbar>
|
||||||
|
<div *ngIf="isFinished(status)">
|
||||||
|
<button *ngIf="status.documentId" class="btn btn-sm btn-outline-primary btn-open" routerLink="/documents/{{status.documentId}}" (click)="dismiss(status)">
|
||||||
|
<small>Open document</small>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" fill="currentColor" class="bi bi-arrow-right-short" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M4 8a.5.5 0 0 1 .5-.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5A.5.5 0 0 1 4 8z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ngb-alert>
|
||||||
|
</ng-template>
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
@import "/src/theme";
|
||||||
|
|
||||||
|
form {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-heading {
|
||||||
|
font-size: 80%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alerts-hidden {
|
||||||
|
.btn {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-open {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dismiss-all {
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-multiple-status .dismiss-all {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .progress {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: auto;
|
||||||
|
mix-blend-mode: soft-light;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
|
|||||||
import { ConsumerStatusService, FileStatus, FileStatusPhase } from 'src/app/services/consumer-status.service';
|
import { ConsumerStatusService, FileStatus, FileStatusPhase } from 'src/app/services/consumer-status.service';
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||||
|
|
||||||
|
const MAX_ALERTS = 5
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-upload-file-widget',
|
selector: 'app-upload-file-widget',
|
||||||
@ -11,6 +12,7 @@ import { DocumentService } from 'src/app/services/rest/document.service';
|
|||||||
styleUrls: ['./upload-file-widget.component.scss']
|
styleUrls: ['./upload-file-widget.component.scss']
|
||||||
})
|
})
|
||||||
export class UploadFileWidgetComponent implements OnInit {
|
export class UploadFileWidgetComponent implements OnInit {
|
||||||
|
alertsExpanded = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
@ -18,7 +20,12 @@ export class UploadFileWidgetComponent implements OnInit {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
getStatus() {
|
getStatus() {
|
||||||
return this.consumerStatusService.getConsumerStatus()
|
return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusesHidden() {
|
||||||
|
if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS) return []
|
||||||
|
else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusUploading() {
|
getStatusUploading() {
|
||||||
@ -41,7 +48,7 @@ export class UploadFileWidgetComponent implements OnInit {
|
|||||||
return status.phase == FileStatusPhase.FAILED || status.phase == FileStatusPhase.SUCCESS
|
return status.phase == FileStatusPhase.FAILED || status.phase == FileStatusPhase.SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
getType(status: FileStatus) {
|
getStatusColor(status: FileStatus) {
|
||||||
switch (status.phase) {
|
switch (status.phase) {
|
||||||
case FileStatusPhase.PROCESSING:
|
case FileStatusPhase.PROCESSING:
|
||||||
case FileStatusPhase.UPLOADING:
|
case FileStatusPhase.UPLOADING:
|
||||||
@ -57,6 +64,10 @@ export class UploadFileWidgetComponent implements OnInit {
|
|||||||
this.consumerStatusService.dismiss(status)
|
this.consumerStatusService.dismiss(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dismissAll() {
|
||||||
|
this.consumerStatusService.dismissAll()
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export class FileStatus {
|
|||||||
case FileStatusPhase.UPLOADING:
|
case FileStatusPhase.UPLOADING:
|
||||||
return this.currentPhaseProgress / this.currentPhaseMaxProgress * 0.2
|
return this.currentPhaseProgress / this.currentPhaseMaxProgress * 0.2
|
||||||
case FileStatusPhase.PROCESSING:
|
case FileStatusPhase.PROCESSING:
|
||||||
return this.currentPhaseProgress / this.currentPhaseMaxProgress * 0.8 + 0.2
|
return (this.currentPhaseProgress / this.currentPhaseMaxProgress * 0.8) + 0.2
|
||||||
case FileStatusPhase.SUCCESS:
|
case FileStatusPhase.SUCCESS:
|
||||||
case FileStatusPhase.FAILED:
|
case FileStatusPhase.FAILED:
|
||||||
return 1.0
|
return 1.0
|
||||||
@ -43,7 +43,7 @@ export class FileStatus {
|
|||||||
updateProgress(status: FileStatusPhase, currentProgress?: number, maxProgress?: number) {
|
updateProgress(status: FileStatusPhase, currentProgress?: number, maxProgress?: number) {
|
||||||
if (status >= this.phase) {
|
if (status >= this.phase) {
|
||||||
this.phase = status
|
this.phase = status
|
||||||
if (currentProgress) {
|
if (currentProgress != undefined) {
|
||||||
this.currentPhaseProgress = currentProgress
|
this.currentPhaseProgress = currentProgress
|
||||||
}
|
}
|
||||||
if (maxProgress) {
|
if (maxProgress) {
|
||||||
@ -132,6 +132,10 @@ export class ConsumerStatusService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dismissAll() {
|
||||||
|
this.consumerStatus = []
|
||||||
|
}
|
||||||
|
|
||||||
onDocumentConsumptionFinished() {
|
onDocumentConsumptionFinished() {
|
||||||
return this.documentConsumptionFinishedSubject
|
return this.documentConsumptionFinishedSubject
|
||||||
}
|
}
|
||||||
|
@ -111,3 +111,7 @@ body {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ngx-file-drop__drop-zone--over {
|
||||||
|
background-color: $primaryFaded !important;
|
||||||
|
}
|
||||||
|
@ -352,6 +352,20 @@ $border-color-dark-mode: #47494f;
|
|||||||
.bg-dark {
|
.bg-dark {
|
||||||
background-color: $bg-light-dark-mode !important;
|
background-color: $bg-light-dark-mode !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ngx-file-drop__drop-zone--over {
|
||||||
|
background-color: darken($primary-dark-mode, 35%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-secondary {
|
||||||
|
background-color: $bg-light-dark-mode;
|
||||||
|
border-color: darken($bg-light-dark-mode, 10%);
|
||||||
|
color: $text-color-dark-mode-accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar.bg-primary {
|
||||||
|
background-color: darken($primary-dark-mode, 5%) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.color-scheme-dark {
|
body.color-scheme-dark {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user