Frontend display custom fields in dasboard views
This commit is contained in:
parent
0f29899b71
commit
cd4cc4d9e1
@ -111,7 +111,7 @@
|
||||
</h6>
|
||||
}
|
||||
<ul class="nav flex-column mb-2" cdkDropList (cdkDropListDropped)="onDrop($event)">
|
||||
@for (view of savedViewService.sidebarViews; track view) {
|
||||
@for (view of savedViewService.sidebarViews; track view.id) {
|
||||
<li class="nav-item w-100 app-link" cdkDrag [cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
|
||||
cdkDragPreviewContainer="parent" cdkDragPreviewClass="navItemDrag" (cdkDragStarted)="onDragStart($event)"
|
||||
(cdkDragEnded)="onDragEnd($event)">
|
||||
|
@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }">
|
||||
@for (v of dashboardViews; track v) {
|
||||
@for (v of dashboardViews; track v.id) {
|
||||
<div class="col">
|
||||
<pngx-saved-view-widget
|
||||
[savedView]="v"
|
||||
|
@ -14,7 +14,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
@for (column of savedView.dashboard_view_table_columns; track column; let i = $index) {
|
||||
@if (columnIsVisible(column)) {
|
||||
@if (visibleColumns.includes(column)) {
|
||||
<th
|
||||
scope="col"
|
||||
[ngClass]="{
|
||||
@ -28,10 +28,10 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (doc of documents; track doc) {
|
||||
<tr (mouseleave)="maybeClosePopover()">
|
||||
@for (doc of documents; track doc.id) {
|
||||
<tr>
|
||||
@for (column of savedView.dashboard_view_table_columns; track column; let i = $index) {
|
||||
@if (columnIsVisible(column)) {
|
||||
@if (visibleColumns.includes(column)) {
|
||||
<td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': i > 1 }">
|
||||
@switch (column) {
|
||||
@case (DashboardViewTableColumn.ADDED) {
|
||||
@ -64,6 +64,31 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD)) {
|
||||
@switch(getCustomFieldDataType(column)) {
|
||||
@case (CustomFieldDataType.Monetary) {
|
||||
{{ getMonetaryCustomFieldValue(doc, column)[0] | currency: getMonetaryCustomFieldValue(doc, column)[1] }}
|
||||
}
|
||||
@case (CustomFieldDataType.Date) {
|
||||
{{ getCustomFieldValue(doc, column) | customDate }}
|
||||
}
|
||||
@case (CustomFieldDataType.Url) {
|
||||
<a [href]="getCustomFieldValue(doc, column)" class="btn-link text-dark text-decoration-none" target="_blank">{{ getCustomFieldValue(doc, column) }}</a>
|
||||
}
|
||||
@case (CustomFieldDataType.DocumentLink) {
|
||||
<div class="d-flex gap-1 flex-wrap">
|
||||
@for (docId of getCustomFieldValue(doc, column); track docId) {
|
||||
<a routerLink="/documents/{{docId}}" class="badge bg-dark text-primary" title="View" i18n-title>
|
||||
<i-bs width="0.9em" height="0.9em" name="file-text"></i-bs> <span>{{ getDocumentTitle(docId) }}</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@default {
|
||||
{{ getCustomFieldValue(doc, column) }}
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (i === savedView.dashboard_view_table_columns.length - 1) {
|
||||
<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 px-4 btn-dark border-dark-subtle"
|
||||
|
@ -40,6 +40,8 @@ import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe'
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { PreviewPopupComponent } from 'src/app/components/common/preview-popup/preview-popup.component'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
import { CustomFieldDataType } from 'src/app/data/custom-field'
|
||||
|
||||
const savedView: SavedView = {
|
||||
id: 1,
|
||||
@ -61,6 +63,10 @@ const savedView: SavedView = {
|
||||
DashboardViewTableColumn.TITLE,
|
||||
DashboardViewTableColumn.TAGS,
|
||||
DashboardViewTableColumn.CORRESPONDENT,
|
||||
DashboardViewTableColumn.DOCUMENT_TYPE,
|
||||
DashboardViewTableColumn.STORAGE_PATH,
|
||||
`${DashboardViewTableColumn.CUSTOM_FIELD}11` as any,
|
||||
`${DashboardViewTableColumn.CUSTOM_FIELD}15` as any,
|
||||
],
|
||||
}
|
||||
|
||||
@ -68,11 +74,35 @@ const documentResults = [
|
||||
{
|
||||
id: 2,
|
||||
title: 'doc2',
|
||||
custom_fields: [
|
||||
{ id: 1, field: 11, created: new Date(), value: 'custom', document: 2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'doc3',
|
||||
correspondent: 0,
|
||||
custom_fields: [],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'doc4',
|
||||
custom_fields: [
|
||||
{ id: 32, field: 3, created: new Date(), value: 'EUR123', document: 4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'doc5',
|
||||
custom_fields: [
|
||||
{
|
||||
id: 22,
|
||||
field: 15,
|
||||
created: new Date(),
|
||||
value: [123, 456, 789],
|
||||
document: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@ -106,6 +136,33 @@ describe('SavedViewWidgetComponent', () => {
|
||||
},
|
||||
CustomDatePipe,
|
||||
DatePipe,
|
||||
{
|
||||
provide: CustomFieldsService,
|
||||
useValue: {
|
||||
listAll: () =>
|
||||
of({
|
||||
all: [3, 11, 15],
|
||||
count: 3,
|
||||
results: [
|
||||
{
|
||||
id: 3,
|
||||
name: 'Custom field 3',
|
||||
data_type: CustomFieldDataType.Monetary,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Custom Field 11',
|
||||
data_type: CustomFieldDataType.String,
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: 'Custom Field 15',
|
||||
data_type: CustomFieldDataType.DocumentLink,
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
@ -289,52 +346,97 @@ describe('SavedViewWidgetComponent', () => {
|
||||
|
||||
it('should check if column is visible including permissions', () => {
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.TITLE)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.TITLE)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.CREATED)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.CREATED)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.ADDED)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.ADDED)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.TAGS)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.TAGS)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.CORRESPONDENT)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.CORRESPONDENT)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.DOCUMENT_TYPE)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.DOCUMENT_TYPE)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.STORAGE_PATH)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.STORAGE_PATH)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.visibleColumns.includes(
|
||||
`${DashboardViewTableColumn.CUSTOM_FIELD}11` as any
|
||||
)
|
||||
).toBeTruthy()
|
||||
|
||||
component.visibleColumns = []
|
||||
jest
|
||||
.spyOn(component.permissionsService, 'currentUserCan')
|
||||
.mockReturnValue(false)
|
||||
component.ngOnInit()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.TITLE)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.CREATED)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.ADDED)
|
||||
).toBeTruthy()
|
||||
expect(component.columnIsVisible(DashboardViewTableColumn.TAGS)).toBeFalsy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.CORRESPONDENT)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.TAGS)
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.DOCUMENT_TYPE)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.CORRESPONDENT)
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
component.columnIsVisible(DashboardViewTableColumn.STORAGE_PATH)
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.DOCUMENT_TYPE)
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
component.visibleColumns.includes(DashboardViewTableColumn.STORAGE_PATH)
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
component.visibleColumns.includes(
|
||||
`${DashboardViewTableColumn.CUSTOM_FIELD}11` as any
|
||||
)
|
||||
).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should display monetary custom field value', () => {
|
||||
expect(
|
||||
component.columnIsVisible('unknown' as DashboardViewTableColumn)
|
||||
).toBeFalsy() // coverage
|
||||
component.getMonetaryCustomFieldValue(
|
||||
documentResults[2],
|
||||
`${DashboardViewTableColumn.CUSTOM_FIELD}3`
|
||||
)
|
||||
).toEqual([123, 'EUR'])
|
||||
expect(
|
||||
component.getMonetaryCustomFieldValue(
|
||||
documentResults[0],
|
||||
`${DashboardViewTableColumn.CUSTOM_FIELD}999`
|
||||
)
|
||||
).toEqual([null, null])
|
||||
})
|
||||
|
||||
it('should retrieve documents for document link columns', () => {
|
||||
const listAllSpy = jest.spyOn(documentService, 'listAll')
|
||||
listAllSpy.mockReturnValue(
|
||||
of({
|
||||
all: [123, 456, 789],
|
||||
count: 3,
|
||||
results: [
|
||||
{ id: 123, title: 'doc123' },
|
||||
{ id: 456, title: 'doc456' },
|
||||
{ id: 789, title: 'doc789' },
|
||||
],
|
||||
})
|
||||
)
|
||||
jest.spyOn(documentService, 'listFiltered').mockReturnValue(
|
||||
of({
|
||||
all: [4, 5],
|
||||
count: 2,
|
||||
results: [documentResults[2], documentResults[3]],
|
||||
})
|
||||
)
|
||||
component.ngOnInit()
|
||||
expect(listAllSpy).toHaveBeenCalledWith(null, false, {
|
||||
id__in: '123,456,789',
|
||||
})
|
||||
fixture.detectChanges()
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('doc123')
|
||||
component.maybeGetDocuments() // coverage
|
||||
})
|
||||
})
|
||||
|
@ -32,6 +32,9 @@ import {
|
||||
PermissionType,
|
||||
PermissionsService,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
||||
import { Results } from 'src/app/data/results'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-saved-view-widget',
|
||||
@ -44,9 +47,12 @@ export class SavedViewWidgetComponent
|
||||
{
|
||||
public DashboardViewMode = DashboardViewMode
|
||||
public DashboardViewTableColumn = DashboardViewTableColumn
|
||||
public CustomFieldDataType = CustomFieldDataType
|
||||
|
||||
loading: boolean = true
|
||||
|
||||
private customFields: CustomField[] = []
|
||||
|
||||
constructor(
|
||||
private documentService: DocumentService,
|
||||
private router: Router,
|
||||
@ -54,7 +60,8 @@ export class SavedViewWidgetComponent
|
||||
private consumerStatusService: ConsumerStatusService,
|
||||
public openDocumentsService: OpenDocumentsService,
|
||||
public documentListViewService: DocumentListViewService,
|
||||
public permissionsService: PermissionsService
|
||||
public permissionsService: PermissionsService,
|
||||
private customFieldService: CustomFieldsService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@ -72,6 +79,14 @@ export class SavedViewWidgetComponent
|
||||
mouseOnPreview = false
|
||||
popoverHidden = true
|
||||
|
||||
visibleColumns: DashboardViewTableColumn[] = [
|
||||
DashboardViewTableColumn.TITLE,
|
||||
DashboardViewTableColumn.CREATED,
|
||||
DashboardViewTableColumn.ADDED,
|
||||
]
|
||||
|
||||
docLinkDocuments: Document[] = []
|
||||
|
||||
ngOnInit(): void {
|
||||
this.reload()
|
||||
this.consumerStatusService
|
||||
@ -80,6 +95,35 @@ export class SavedViewWidgetComponent
|
||||
.subscribe(() => {
|
||||
this.reload()
|
||||
})
|
||||
|
||||
if (
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.CustomField
|
||||
)
|
||||
) {
|
||||
this.customFieldService
|
||||
.listAll()
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe((customFields) => {
|
||||
this.customFields = customFields.results
|
||||
this.maybeGetDocuments()
|
||||
})
|
||||
}
|
||||
|
||||
this.savedView.dashboard_view_table_columns?.forEach((column) => {
|
||||
let type: PermissionType = Object.values(PermissionType).find((t) =>
|
||||
t.includes(column)
|
||||
)
|
||||
if (column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD)) {
|
||||
type = PermissionType.CustomField
|
||||
}
|
||||
if (
|
||||
type &&
|
||||
this.permissionsService.currentUserCan(PermissionAction.View, type)
|
||||
)
|
||||
this.visibleColumns.push(column)
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@ -102,6 +146,7 @@ export class SavedViewWidgetComponent
|
||||
.subscribe((result) => {
|
||||
this.loading = false
|
||||
this.documents = result.results
|
||||
this.maybeGetDocuments()
|
||||
})
|
||||
}
|
||||
|
||||
@ -204,26 +249,73 @@ export class SavedViewWidgetComponent
|
||||
}, 300)
|
||||
}
|
||||
|
||||
public columnIsVisible(column: DashboardViewTableColumn): boolean {
|
||||
if (
|
||||
[
|
||||
DashboardViewTableColumn.TITLE,
|
||||
DashboardViewTableColumn.CREATED,
|
||||
DashboardViewTableColumn.ADDED,
|
||||
].includes(column)
|
||||
) {
|
||||
return true
|
||||
} else {
|
||||
const type: PermissionType = Object.values(PermissionType).find((t) =>
|
||||
t.includes(column)
|
||||
public getColumnTitle(column: DashboardViewTableColumn): string {
|
||||
if (column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD)) {
|
||||
const id = column.split('_')[2]
|
||||
return this.customFields.find((c) => c.id === parseInt(id))?.name
|
||||
}
|
||||
return DASHBOARD_VIEW_TABLE_COLUMNS.find((c) => c.id === column)?.name
|
||||
}
|
||||
|
||||
public getCustomFieldDataType(column_id: string): string {
|
||||
const customFieldId = parseInt(column_id.split('_')[2])
|
||||
return this.customFields.find((cf) => cf.id === customFieldId)?.data_type
|
||||
}
|
||||
|
||||
public getCustomFieldValue(document: Document, column_id: string): any {
|
||||
const customFieldId = parseInt(column_id.split('_')[2])
|
||||
return document.custom_fields.find((cf) => cf.field === customFieldId)
|
||||
?.value
|
||||
}
|
||||
|
||||
public getMonetaryCustomFieldValue(
|
||||
document: Document,
|
||||
column_id: string
|
||||
): Array<number | string> {
|
||||
const value = this.getCustomFieldValue(document, column_id)
|
||||
if (!value) return [null, null]
|
||||
const currencyCode = value.match(/[A-Z]{3}/)?.[0]
|
||||
const amount = parseFloat(value.replace(currencyCode, ''))
|
||||
return [amount, currencyCode]
|
||||
}
|
||||
|
||||
maybeGetDocuments() {
|
||||
// retrieve documents for document link columns
|
||||
if (this.docLinkDocuments.length) return
|
||||
let docIds = []
|
||||
let docLinkColumns = []
|
||||
this.savedView.dashboard_view_table_columns
|
||||
?.filter((column) =>
|
||||
column.startsWith(DashboardViewTableColumn.CUSTOM_FIELD)
|
||||
)
|
||||
return type
|
||||
? this.permissionsService.currentUserCan(PermissionAction.View, type)
|
||||
: false
|
||||
.forEach((column) => {
|
||||
if (
|
||||
this.getCustomFieldDataType(column) ===
|
||||
CustomFieldDataType.DocumentLink
|
||||
) {
|
||||
docLinkColumns.push(column)
|
||||
}
|
||||
})
|
||||
this.documents.forEach((doc) => {
|
||||
docLinkColumns.forEach((column) => {
|
||||
const docs: number[] = this.getCustomFieldValue(doc, column)
|
||||
if (docs) {
|
||||
docIds = docIds.concat(docs)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (docIds.length) {
|
||||
this.documentService
|
||||
.listAll(null, false, { id__in: docIds.join(',') })
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe((result: Results<Document>) => {
|
||||
this.docLinkDocuments = result.results
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public getColumnTitle(column: DashboardViewTableColumn): string {
|
||||
return DASHBOARD_VIEW_TABLE_COLUMNS.find((c) => c.id === column)?.name
|
||||
public getDocumentTitle(documentId: number): string {
|
||||
return this.docLinkDocuments.find((doc) => doc.id === documentId)?.title
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ export enum DashboardViewTableColumn {
|
||||
CORRESPONDENT = 'correspondent',
|
||||
DOCUMENT_TYPE = 'documenttype',
|
||||
STORAGE_PATH = 'storagepath',
|
||||
CUSTOM_FIELD = 'custom_field_',
|
||||
}
|
||||
|
||||
export const DASHBOARD_VIEW_TABLE_COLUMNS = [
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||
import { HttpClient } from '@angular/common/http'
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service'
|
||||
import { Observable } from 'rxjs'
|
||||
import { CustomField } from 'src/app/data/custom-field'
|
||||
import { CustomFieldInstance } from 'src/app/data/custom-field-instance'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
Loading…
x
Reference in New Issue
Block a user