mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
frontend support for suggestions #264
This commit is contained in:
parent
ddcc0883eb
commit
d22d9a023c
@ -146,35 +146,35 @@
|
||||
<source>Confirm delete</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">192</context>
|
||||
<context context-type="linenumber">199</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5382975254277698192" datatype="html">
|
||||
<source>Do you really want to delete document "<x id="PH" equiv-text="this.document.title"/>"?</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">193</context>
|
||||
<context context-type="linenumber">200</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6691075929777935948" datatype="html">
|
||||
<source>The files for this document will be deleted permanently. This operation cannot be undone.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">194</context>
|
||||
<context context-type="linenumber">201</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="719892092227206532" datatype="html">
|
||||
<source>Delete document</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">196</context>
|
||||
<context context-type="linenumber">203</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1844801255494293730" datatype="html">
|
||||
<source>Error deleting document: <x id="PH" equiv-text="JSON.stringify(error)"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">203</context>
|
||||
<context context-type="linenumber">210</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="826b25211922a1b46436589233cb6f1a163d89b7" datatype="html">
|
||||
@ -1288,6 +1288,13 @@
|
||||
<context context-type="linenumber">27</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="993900b5f9b0d14c7dbf3afd308afcfc1be5c356" datatype="html">
|
||||
<source>Suggested:</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/input/select/select.component.html</context>
|
||||
<context context-type="linenumber">26</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="27d158b47717ff9305d19866960418c603f19d55" datatype="html">
|
||||
<source>Save current view</source>
|
||||
<context-group purpose="location">
|
||||
@ -1509,49 +1516,49 @@
|
||||
<source>ASN</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">16</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2691296884221415710" datatype="html">
|
||||
<source>Correspondent</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">18</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5701618810648052610" datatype="html">
|
||||
<source>Title</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">18</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5066119607229701477" datatype="html">
|
||||
<source>Document type</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4207916966377787111" datatype="html">
|
||||
<source>Created</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="231679111972850796" datatype="html">
|
||||
<source>Added</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3553216189604488439" datatype="html">
|
||||
<source>Modified</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2056433880533904076" datatype="html">
|
||||
|
@ -22,4 +22,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
|
||||
<small *ngIf="getSuggestions().length > 0">
|
||||
<span i18n>Suggested:</span>
|
||||
<ng-container *ngFor="let s of getSuggestions()">
|
||||
<a (click)="value = s.id; onChange(value)" [routerLink]="">{{s.name}}</a>
|
||||
</ng-container>
|
||||
|
||||
|
||||
</small>
|
||||
</div>
|
||||
|
@ -30,11 +30,22 @@ export class SelectComponent extends AbstractInputComponent<number> {
|
||||
@Input()
|
||||
allowNull: boolean = false
|
||||
|
||||
@Input()
|
||||
suggestions: number[]
|
||||
|
||||
@Output()
|
||||
createNew = new EventEmitter()
|
||||
|
||||
|
||||
showPlusButton(): boolean {
|
||||
return this.createNew.observers.length > 0
|
||||
}
|
||||
|
||||
getSuggestions() {
|
||||
if (this.suggestions && this.items) {
|
||||
return this.suggestions.filter(id => id != this.value).map(id => this.items.find(item => item.id == id))
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,30 +2,25 @@
|
||||
<label for="tags" i18n>Tags</label>
|
||||
|
||||
<div class="input-group flex-nowrap">
|
||||
<ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="displayValue"
|
||||
<ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="value"
|
||||
[multiple]="true"
|
||||
[closeOnSelect]="false"
|
||||
[clearSearchOnAdd]="true"
|
||||
[disabled]="disabled"
|
||||
[hideSelected]="true"
|
||||
(change)="ngSelectChange()">
|
||||
(change)="onChange(value)"
|
||||
(blur)="onTouched()">
|
||||
|
||||
<ng-template ng-label-tmp let-item="item">
|
||||
<span class="tag-wrap tag-wrap-delete" (click)="removeTag(item.id)">
|
||||
<svg width="1.2em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||
</svg>
|
||||
<app-tag style="background-color: none;" [tag]="getTag(item.id)"></app-tag>
|
||||
<app-tag *ngIf="item.id && tags" style="background-color: none;" [tag]="getTag(item.id)"></app-tag>
|
||||
</span>
|
||||
</ng-template>
|
||||
<ng-template ng-option-tmp let-item="item" let-index="index" let-search="searchTerm">
|
||||
<div class="tag-wrap">
|
||||
<div class="selected-icon d-inline-block mr-1">
|
||||
<svg *ngIf="displayValue.includes(item.id)" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#check"/>
|
||||
</svg>
|
||||
</div>
|
||||
<app-tag class="mr-2" [tag]="getTag(item.id)"></app-tag>
|
||||
<app-tag *ngIf="item.id && tags" class="mr-2" [tag]="getTag(item.id)"></app-tag>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-select>
|
||||
@ -39,5 +34,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted" *ngIf="hint">{{hint}}</small>
|
||||
<small *ngIf="getSuggestions().length > 0">
|
||||
<span i18n>Suggested:</span>
|
||||
<ng-container *ngFor="let tag of getSuggestions()">
|
||||
<a (click)="addTag(tag.id)" [routerLink]="">{{tag.name}}</a>
|
||||
</ng-container>
|
||||
|
||||
|
||||
</small>
|
||||
|
||||
</div>
|
||||
|
@ -26,9 +26,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
|
||||
writeValue(newValue: number[]): void {
|
||||
this.value = newValue
|
||||
if (this.tags) {
|
||||
this.displayValue = newValue
|
||||
}
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
@ -43,7 +40,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
ngOnInit(): void {
|
||||
this.tagService.listAll().subscribe(result => {
|
||||
this.tags = result.results
|
||||
this.displayValue = this.value
|
||||
})
|
||||
}
|
||||
|
||||
@ -53,23 +49,28 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
@Input()
|
||||
hint
|
||||
|
||||
value: number[]
|
||||
@Input()
|
||||
suggestions: number[]
|
||||
|
||||
displayValue: number[] = []
|
||||
value: number[]
|
||||
|
||||
tags: PaperlessTag[]
|
||||
|
||||
getTag(id) {
|
||||
return this.tags.find(tag => tag.id == id)
|
||||
if (this.tags) {
|
||||
return this.tags.find(tag => tag.id == id)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
removeTag(id) {
|
||||
let index = this.displayValue.indexOf(id)
|
||||
let index = this.value.indexOf(id)
|
||||
if (index > -1) {
|
||||
let oldValue = this.displayValue
|
||||
let oldValue = this.value
|
||||
oldValue.splice(index, 1)
|
||||
this.displayValue = [...oldValue]
|
||||
this.onChange(this.displayValue)
|
||||
this.value = [...oldValue]
|
||||
this.onChange(this.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,15 +80,23 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||
modal.componentInstance.success.subscribe(newTag => {
|
||||
this.tagService.listAll().subscribe(tags => {
|
||||
this.tags = tags.results
|
||||
this.displayValue = [...this.displayValue, newTag.id]
|
||||
this.onChange(this.displayValue)
|
||||
this.value = [...this.value, newTag.id]
|
||||
this.onChange(this.value)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ngSelectChange() {
|
||||
this.value = this.displayValue
|
||||
this.onChange(this.displayValue)
|
||||
getSuggestions() {
|
||||
if (this.suggestions && this.tags) {
|
||||
return this.suggestions.filter(id => !this.value.includes(id)).map(id => this.tags.find(tag => tag.id == id))
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
addTag(id) {
|
||||
this.value = [...this.value, id]
|
||||
this.onChange(this.value)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,10 +60,10 @@
|
||||
<app-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" formControlName='archive_serial_number'></app-input-number>
|
||||
<app-input-date-time i18n-titleDate titleDate="Date created" formControlName="created"></app-input-date-time>
|
||||
<app-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true"
|
||||
(createNew)="createCorrespondent()"></app-input-select>
|
||||
(createNew)="createCorrespondent()" [suggestions]="suggestions?.correspondents"></app-input-select>
|
||||
<app-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true"
|
||||
(createNew)="createDocumentType()"></app-input-select>
|
||||
<app-input-tags formControlName="tags"></app-input-tags>
|
||||
(createNew)="createDocumentType()" [suggestions]="suggestions?.document_types"></app-input-select>
|
||||
<app-input-tags formControlName="tags" [suggestions]="suggestions?.tags"></app-input-tags>
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
@ -145,6 +145,6 @@
|
||||
<ng-container *ngIf="getContentType() == 'text/plain'">
|
||||
<object [data]="previewUrl | safe" type="text/plain" class="preview-sticky" width="100%"></object>
|
||||
</ng-container>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,6 +19,7 @@ import { PDFDocumentProxy } from 'ng2-pdf-viewer';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
import { TextComponent } from '../common/input/text/text.component';
|
||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-document-detail',
|
||||
@ -40,6 +41,8 @@ export class DocumentDetailComponent implements OnInit {
|
||||
documentId: number
|
||||
document: PaperlessDocument
|
||||
metadata: PaperlessDocumentMetadata
|
||||
suggestions: PaperlessDocumentSuggestions
|
||||
|
||||
title: string
|
||||
previewUrl: string
|
||||
downloadUrl: string
|
||||
@ -95,6 +98,7 @@ export class DocumentDetailComponent implements OnInit {
|
||||
this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
|
||||
this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId)
|
||||
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true)
|
||||
this.suggestions = null
|
||||
if (this.openDocumentService.getOpenDocument(this.documentId)) {
|
||||
this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId))
|
||||
} else {
|
||||
@ -112,6 +116,9 @@ export class DocumentDetailComponent implements OnInit {
|
||||
this.documentsService.getMetadata(doc.id).subscribe(result => {
|
||||
this.metadata = result
|
||||
})
|
||||
this.documentsService.getSuggestions(doc.id).subscribe(result => {
|
||||
this.suggestions = result
|
||||
})
|
||||
this.title = this.documentTitlePipe.transform(doc.title)
|
||||
this.documentForm.patchValue(doc)
|
||||
}
|
||||
|
9
src-ui/src/app/data/paperless-document-suggestions.ts
Normal file
9
src-ui/src/app/data/paperless-document-suggestions.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface PaperlessDocumentSuggestions {
|
||||
|
||||
tags?: number[]
|
||||
|
||||
correspondents?: number[]
|
||||
|
||||
document_types?: number[]
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ import { CorrespondentService } from './correspondent.service';
|
||||
import { DocumentTypeService } from './document-type.service';
|
||||
import { TagService } from './tag.service';
|
||||
import { FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type';
|
||||
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions';
|
||||
|
||||
export const DOCUMENT_SORT_FIELDS = [
|
||||
{ field: 'archive_serial_number', name: $localize`ASN` },
|
||||
@ -129,4 +130,8 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
|
||||
return this.http.post<SelectionData>(this.getResourceUrl(null, 'selection_data'), {"documents": ids})
|
||||
}
|
||||
|
||||
getSuggestions(id: number): Observable<PaperlessDocumentSuggestions> {
|
||||
return this.http.get<PaperlessDocumentSuggestions>(this.getResourceUrl(id, 'suggestions'))
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user