Basic toggling of fields
This commit is contained in:
parent
4ffe5d34f0
commit
af94eed042
@ -140,6 +140,7 @@ import {
|
|||||||
calendar,
|
calendar,
|
||||||
calendarEvent,
|
calendarEvent,
|
||||||
cardChecklist,
|
cardChecklist,
|
||||||
|
cardHeading,
|
||||||
caretDown,
|
caretDown,
|
||||||
caretUp,
|
caretUp,
|
||||||
chatLeftText,
|
chatLeftText,
|
||||||
@ -234,6 +235,7 @@ const icons = {
|
|||||||
calendar,
|
calendar,
|
||||||
calendarEvent,
|
calendarEvent,
|
||||||
cardChecklist,
|
cardChecklist,
|
||||||
|
cardHeading,
|
||||||
caretDown,
|
caretDown,
|
||||||
caretUp,
|
caretUp,
|
||||||
chatLeftText,
|
chatLeftText,
|
||||||
|
@ -351,23 +351,23 @@
|
|||||||
</pngx-confirm-button>
|
</pngx-confirm-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (savedViewGroup.get(view.id.toString()).get('show_on_dashboard').value) {
|
<div class="row">
|
||||||
<div class="row">
|
@if (savedViewGroup.get(view.id.toString()).get('show_on_dashboard').value) {
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-number i18n-title title="Widget list limit" [showAdd]="false" formControlName="page_size"></pngx-input-number>
|
<pngx-input-number i18n-title title="Widget list limit" [showAdd]="false" formControlName="page_size"></pngx-input-number>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label class="form-label" for="display_mode_{{view.id}}" i18n>Widget display</label>
|
<label class="form-label" for="display_mode_{{view.id}}" i18n>Widget display</label>
|
||||||
<select class="form-select" formControlName="display_mode">
|
<select class="form-select" formControlName="display_mode">
|
||||||
<option [ngValue]="DashboardViewMode.TABLE" i18n>Table</option>
|
<option [ngValue]="DashboardViewMode.TABLE" i18n>Table</option>
|
||||||
<option [ngValue]="DashboardViewMode.SMALL_CARDS" i18n>Cards</option>
|
<option [ngValue]="DashboardViewMode.SMALL_CARDS" i18n>Cards</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@if (savedViewGroup.get(view.id.toString()).get('display_mode').value === DashboardViewMode.TABLE && documentDisplayFields) {
|
}
|
||||||
|
@if (documentDisplayFields) {
|
||||||
<pngx-input-drag-drop-select i18n-title title="Show" [items]="documentDisplayFields" formControlName="document_display_fields"></pngx-input-drag-drop-select>
|
<pngx-input-drag-drop-select i18n-title title="Show" [items]="documentDisplayFields" formControlName="document_display_fields"></pngx-input-drag-drop-select>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,9 @@ export class SettingsComponent
|
|||||||
|
|
||||||
savedViews: SavedView[]
|
savedViews: SavedView[]
|
||||||
SettingsNavIDs = SettingsNavIDs
|
SettingsNavIDs = SettingsNavIDs
|
||||||
documentDisplayFields: any[]
|
get documentDisplayFields() {
|
||||||
|
return this.settings.allDocumentDisplayFields
|
||||||
|
}
|
||||||
|
|
||||||
store: BehaviorSubject<any>
|
store: BehaviorSubject<any>
|
||||||
storeSub: Subscription
|
storeSub: Subscription
|
||||||
@ -225,24 +227,6 @@ export class SettingsComponent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
this.permissionsService.currentUserCan(
|
|
||||||
PermissionAction.View,
|
|
||||||
PermissionType.CustomField
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.customFieldsService.listAll().subscribe((r) => {
|
|
||||||
this.documentDisplayFields = DOCUMENT_DISPLAY_FIELDS.concat(
|
|
||||||
r.results.map((field) => {
|
|
||||||
return {
|
|
||||||
id: `${DocumentDisplayField.CUSTOM_FIELD}${field.id}` as any,
|
|
||||||
name: field.name,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.activatedRoute.paramMap.subscribe((paramMap) => {
|
this.activatedRoute.paramMap.subscribe((paramMap) => {
|
||||||
const section = paramMap.get('section')
|
const section = paramMap.get('section')
|
||||||
if (section) {
|
if (section) {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@for (column of savedView.document_display_fields; track column; let i = $index) {
|
@for (column of savedView.document_display_fields; track column; let i = $index) {
|
||||||
@if (visibleColumns.includes(column)) {
|
@if (activeDisplayFields.has(column)) {
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
@ -31,7 +31,7 @@
|
|||||||
@for (doc of documents; track doc.id) {
|
@for (doc of documents; track doc.id) {
|
||||||
<tr>
|
<tr>
|
||||||
@for (column of savedView.document_display_fields; track column; let i = $index) {
|
@for (column of savedView.document_display_fields; track column; let i = $index) {
|
||||||
@if (visibleColumns.includes(column)) {
|
@if (activeDisplayFields.has(column)) {
|
||||||
<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': i > 1 }">
|
||||||
@switch (column) {
|
@switch (column) {
|
||||||
@case (DashboardViewTableColumn.ADDED) {
|
@case (DashboardViewTableColumn.ADDED) {
|
||||||
@ -114,7 +114,16 @@
|
|||||||
} @else if (documents.length && savedView.display_mode === DashboardViewMode.SMALL_CARDS) {
|
} @else if (documents.length && savedView.display_mode === DashboardViewMode.SMALL_CARDS) {
|
||||||
<div 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) {
|
||||||
<pngx-document-card-small class="p-0" (dblClickDocument)="openDocumentDetail(d)" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickStoragePath)="clickStoragePath($event)" (clickDocumentType)="clickDocumentType($event)"></pngx-document-card-small>
|
<pngx-document-card-small
|
||||||
|
class="p-0"
|
||||||
|
(dblClickDocument)="openDocumentDetail(d)"
|
||||||
|
[document]="d"
|
||||||
|
[displayFields]="activeDisplayFields"
|
||||||
|
(clickTag)="clickTag($event)"
|
||||||
|
(clickCorrespondent)="clickCorrespondent($event)"
|
||||||
|
(clickStoragePath)="clickStoragePath($event)"
|
||||||
|
(clickDocumentType)="clickDocumentType($event)">
|
||||||
|
</pngx-document-card-small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
|
@ -344,51 +344,51 @@ describe('SavedViewWidgetComponent', () => {
|
|||||||
|
|
||||||
it('should check if column is visible including permissions', () => {
|
it('should check if column is visible including permissions', () => {
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.TITLE)
|
component.activeDisplayFields.includes(DocumentDisplayField.TITLE)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.CREATED)
|
component.activeDisplayFields.includes(DocumentDisplayField.CREATED)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.ADDED)
|
component.activeDisplayFields.includes(DocumentDisplayField.ADDED)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.TAGS)
|
component.activeDisplayFields.includes(DocumentDisplayField.TAGS)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.CORRESPONDENT)
|
component.activeDisplayFields.includes(DocumentDisplayField.CORRESPONDENT)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.DOCUMENT_TYPE)
|
component.activeDisplayFields.includes(DocumentDisplayField.DOCUMENT_TYPE)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.STORAGE_PATH)
|
component.activeDisplayFields.includes(DocumentDisplayField.STORAGE_PATH)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(
|
component.activeDisplayFields.includes(
|
||||||
`${DocumentDisplayField.CUSTOM_FIELD}11` as any
|
`${DocumentDisplayField.CUSTOM_FIELD}11` as any
|
||||||
)
|
)
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
|
|
||||||
component.visibleColumns = []
|
component.activeDisplayFields = []
|
||||||
jest
|
jest
|
||||||
.spyOn(component.permissionsService, 'currentUserCan')
|
.spyOn(component.permissionsService, 'currentUserCan')
|
||||||
.mockReturnValue(false)
|
.mockReturnValue(false)
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.TAGS)
|
component.activeDisplayFields.includes(DocumentDisplayField.TAGS)
|
||||||
).toBeFalsy()
|
).toBeFalsy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.CORRESPONDENT)
|
component.activeDisplayFields.includes(DocumentDisplayField.CORRESPONDENT)
|
||||||
).toBeFalsy()
|
).toBeFalsy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.DOCUMENT_TYPE)
|
component.activeDisplayFields.includes(DocumentDisplayField.DOCUMENT_TYPE)
|
||||||
).toBeFalsy()
|
).toBeFalsy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(DocumentDisplayField.STORAGE_PATH)
|
component.activeDisplayFields.includes(DocumentDisplayField.STORAGE_PATH)
|
||||||
).toBeFalsy()
|
).toBeFalsy()
|
||||||
expect(
|
expect(
|
||||||
component.visibleColumns.includes(
|
component.activeDisplayFields.includes(
|
||||||
`${DocumentDisplayField.CUSTOM_FIELD}11` as any
|
`${DocumentDisplayField.CUSTOM_FIELD}11` as any
|
||||||
)
|
)
|
||||||
).toBeFalsy()
|
).toBeFalsy()
|
||||||
|
@ -79,11 +79,11 @@ export class SavedViewWidgetComponent
|
|||||||
mouseOnPreview = false
|
mouseOnPreview = false
|
||||||
popoverHidden = true
|
popoverHidden = true
|
||||||
|
|
||||||
visibleColumns: DocumentDisplayField[] = [
|
activeDisplayFields: Set<DocumentDisplayField> = new Set([
|
||||||
DocumentDisplayField.TITLE,
|
DocumentDisplayField.TITLE,
|
||||||
DocumentDisplayField.CREATED,
|
DocumentDisplayField.CREATED,
|
||||||
DocumentDisplayField.ADDED,
|
DocumentDisplayField.ADDED,
|
||||||
]
|
])
|
||||||
|
|
||||||
docLinkDocuments: Document[] = []
|
docLinkDocuments: Document[] = []
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ export class SavedViewWidgetComponent
|
|||||||
type &&
|
type &&
|
||||||
this.permissionsService.currentUserCan(PermissionAction.View, type)
|
this.permissionsService.currentUserCan(PermissionAction.View, type)
|
||||||
)
|
)
|
||||||
this.visibleColumns.push(column)
|
this.activeDisplayFields.add(column)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<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">
|
||||||
@if (document.correspondent) {
|
@if (displayFields.has(DocumentDisplayField.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>
|
||||||
} @else {
|
} @else {
|
||||||
@ -23,9 +23,13 @@
|
|||||||
}
|
}
|
||||||
:
|
:
|
||||||
}
|
}
|
||||||
{{document.title | documentTitle}}
|
@if (displayFields.has(DocumentDisplayField.TITLE)) {
|
||||||
@for (t of document.tags$ | async; track t) {
|
{{document.title | documentTitle}}
|
||||||
<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>
|
}
|
||||||
|
@if (displayFields.has(DocumentDisplayField.TAGS)) {
|
||||||
|
@for (t of document.tags$ | async; track t) {
|
||||||
|
<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>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
@ -66,44 +70,49 @@
|
|||||||
</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 (notesEnabled && document.notes.length) {
|
@if (displayFields.has(DocumentDisplayField.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 i18n>{{document.notes.length}} Notes</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="chat-left-text"></i-bs><small i18n>{{document.notes.length}} Notes</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.document_type) {
|
@if (displayFields.has(DocumentDisplayField.DOCUMENT_TYPE) && document.document_type) {
|
||||||
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by document type" i18n-title
|
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by document type" i18n-title
|
||||||
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="file-earmark"></i-bs><small>{{(document.document_type$ | async)?.name}}</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="file-earmark"></i-bs><small>{{(document.document_type$ | async)?.name}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.storage_path) {
|
@if (displayFields.has(DocumentDisplayField.STORAGE_PATH) && document.storage_path) {
|
||||||
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by storage path" i18n-title
|
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by storage path" i18n-title
|
||||||
(click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
|
(click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.storage_path$ | async)?.name}}</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.storage_path$ | async)?.name}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.archive_serial_number | isNumber) {
|
@if (displayFields.has(DocumentDisplayField.ASN) && document.archive_serial_number | isNumber) {
|
||||||
<div class="list-group-item me-2 bg-light text-dark p-1 border-0 d-flex align-items-center">
|
<div class="list-group-item me-2 bg-light text-dark p-1 border-0 d-flex align-items-center">
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="upc-scan"></i-bs><small>#{{document.archive_serial_number}}</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="upc-scan"></i-bs><small>#{{document.archive_serial_number}}</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<ng-template #dateTooltip>
|
@if (displayFields.has(DocumentDisplayField.CREATED) || displayFields.has(DocumentDisplayField.ADDED)) {
|
||||||
<div class="d-flex flex-column text-light">
|
<ng-template #dateTooltip>
|
||||||
<span i18n>Created: {{ document.created | customDate }}</span>
|
<div class="d-flex flex-column text-light">
|
||||||
<span i18n>Added: {{ document.added | customDate }}</span>
|
<span i18n>Created: {{ document.created | customDate }}</span>
|
||||||
<span i18n>Modified: {{ document.modified | customDate }}</span>
|
<span i18n>Added: {{ document.added | customDate }}</span>
|
||||||
|
<span i18n>Modified: {{ document.modified | customDate }}</span>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center" [ngbTooltip]="dateTooltip">
|
||||||
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="calendar-event"></i-bs><small>
|
||||||
|
@if (displayFields.has(DocumentDisplayField.CREATED)) { {{document.created_date | customDate:'mediumDate'}} }
|
||||||
|
@else { {{document.added | customDate:'mediumDate'}} }
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
}
|
||||||
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center" [ngbTooltip]="dateTooltip">
|
@if (displayFields.has(DocumentDisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="calendar-event"></i-bs><small>{{document.created_date | customDate:'mediumDate'}}</small>
|
|
||||||
</div>
|
|
||||||
@if (document.owner && document.owner !== settingsService.currentUser.id) {
|
|
||||||
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center">
|
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center">
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="person-fill-lock"></i-bs><small>{{document.owner | username}}</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="person-fill-lock"></i-bs><small>{{document.owner | username}}</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (document.is_shared_by_requester) {
|
@if (displayFields.has(DocumentDisplayField.SHARED) && document.is_shared_by_requester) {
|
||||||
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center">
|
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center">
|
||||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="people-fill"></i-bs><small i18n>Shared</small>
|
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="people-fill"></i-bs><small i18n>Shared</small>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ import { SettingsService } from 'src/app/services/settings.service'
|
|||||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
|
import { DocumentDisplayField } from 'src/app/data/saved-view'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-document-card-large',
|
selector: 'pngx-document-card-large',
|
||||||
@ -18,6 +19,8 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission
|
|||||||
styleUrls: ['./document-card-large.component.scss'],
|
styleUrls: ['./document-card-large.component.scss'],
|
||||||
})
|
})
|
||||||
export class DocumentCardLargeComponent extends ComponentWithPermissions {
|
export class DocumentCardLargeComponent extends ComponentWithPermissions {
|
||||||
|
DocumentDisplayField = DocumentDisplayField
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
public settingsService: SettingsService
|
public settingsService: SettingsService
|
||||||
@ -28,6 +31,9 @@ export class DocumentCardLargeComponent extends ComponentWithPermissions {
|
|||||||
@Input()
|
@Input()
|
||||||
selected = false
|
selected = false
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
displayFields: Set<DocumentDisplayField>
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
toggleSelected = new EventEmitter()
|
toggleSelected = new EventEmitter()
|
||||||
|
|
||||||
|
@ -10,19 +10,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tags d-flex flex-column text-end position-absolute me-1 fs-6">
|
@if (displayFields?.has(DocumentDisplayField.TAGS)) {
|
||||||
@for (t of getTagsLimited$() | async; track t) {
|
<div class="tags d-flex flex-column text-end position-absolute me-1 fs-6">
|
||||||
<pngx-tag [tag]="t" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="true" linkTitle="Toggle tag filter" i18n-linkTitle></pngx-tag>
|
@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>
|
||||||
@if (moreTags) {
|
}
|
||||||
<div>
|
@if (moreTags) {
|
||||||
<span class="badge text-dark">+ {{moreTags}}</span>
|
<div>
|
||||||
</div>
|
<span class="badge text-dark">+ {{moreTags}}</span>
|
||||||
}
|
</div>
|
||||||
</div>
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (notesEnabled && document.notes.length) {
|
@if (displayFields.has(DocumentDisplayField.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>
|
||||||
@ -32,54 +34,61 @@
|
|||||||
|
|
||||||
<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.correspondent) {
|
@if (displayFields.has(DocumentDisplayField.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>:
|
||||||
}
|
}
|
||||||
{{document.title | documentTitle}}
|
@if (displayFields.has(DocumentDisplayField.TITLE)) {
|
||||||
|
{{document.title | documentTitle}}
|
||||||
|
}
|
||||||
</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.document_type) {
|
@if (displayFields.has(DocumentDisplayField.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()">
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="file-earmark"></i-bs>
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="file-earmark"></i-bs>
|
||||||
<small>{{(document.document_type$ | async)?.name ?? privateName}}</small>
|
<small>{{(document.document_type$ | async)?.name ?? privateName}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (document.storage_path) {
|
@if (displayFields.has(DocumentDisplayField.STORAGE_PATH) && document.storage_path) {
|
||||||
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle storage path filter" i18n-title
|
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle storage path filter" i18n-title
|
||||||
(click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
|
(click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
|
||||||
<small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
|
<small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
|
@if (displayFields.has(DocumentDisplayField.CREATED) || displayFields.has(DocumentDisplayField.ADDED)) {
|
||||||
<ng-template #dateTooltip>
|
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
|
||||||
<div class="d-flex flex-column text-light">
|
<ng-template #dateTooltip>
|
||||||
<span i18n>Created: {{ document.created | customDate }}</span>
|
<div class="d-flex flex-column text-light">
|
||||||
<span i18n>Added: {{ document.added | customDate }}</span>
|
<span i18n>Created: {{ document.created | customDate }}</span>
|
||||||
<span i18n>Modified: {{ document.modified | customDate }}</span>
|
<span i18n>Added: {{ document.added | customDate }}</span>
|
||||||
|
<span i18n>Modified: {{ document.modified | customDate }}</span>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
|
||||||
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
|
||||||
|
<small>
|
||||||
|
@if (displayFields.has(DocumentDisplayField.CREATED)) { {{document.created | customDate:'mediumDate'}} }
|
||||||
|
@else { {{document.added | customDate:'mediumDate'}} }
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
|
||||||
<div class="ps-0 p-1" placement="top" [ngbTooltip]="dateTooltip">
|
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="calendar-event"></i-bs>
|
|
||||||
<small>{{document.created_date | customDate:'mediumDate'}}</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
@if (document.archive_serial_number | isNumber) {
|
@if (displayFields.has(DocumentDisplayField.ASN) && document.archive_serial_number | isNumber) {
|
||||||
<div class="ps-0 p-1">
|
<div class="ps-0 p-1">
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="upc-scan"></i-bs>
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="upc-scan"></i-bs>
|
||||||
<small>#{{document.archive_serial_number}}</small>
|
<small>#{{document.archive_serial_number}}</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (document.owner && document.owner !== settingsService.currentUser.id) {
|
@if (displayFields.has(DocumentDisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
|
||||||
<div class="ps-0 p-1">
|
<div class="ps-0 p-1">
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs>
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs>
|
||||||
<small>{{document.owner | username}}</small>
|
<small>{{document.owner | username}}</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (document.is_shared_by_requester) {
|
@if (displayFields.has(DocumentDisplayField.SHARED) && document.is_shared_by_requester) {
|
||||||
<div class="ps-0 p-1">
|
<div class="ps-0 p-1">
|
||||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="people-fill"></i-bs>
|
<i-bs width="1em" height="1em" class="me-2 text-muted" name="people-fill"></i-bs>
|
||||||
<small i18n>Shared</small>
|
<small i18n>Shared</small>
|
||||||
|
@ -12,6 +12,7 @@ import { SettingsService } from 'src/app/services/settings.service'
|
|||||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
|
import { DocumentDisplayField } from 'src/app/data/saved-view'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-document-card-small',
|
selector: 'pngx-document-card-small',
|
||||||
@ -19,6 +20,8 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission
|
|||||||
styleUrls: ['./document-card-small.component.scss'],
|
styleUrls: ['./document-card-small.component.scss'],
|
||||||
})
|
})
|
||||||
export class DocumentCardSmallComponent extends ComponentWithPermissions {
|
export class DocumentCardSmallComponent extends ComponentWithPermissions {
|
||||||
|
DocumentDisplayField = DocumentDisplayField
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
public settingsService: SettingsService
|
public settingsService: SettingsService
|
||||||
@ -35,6 +38,9 @@ export class DocumentCardSmallComponent extends ComponentWithPermissions {
|
|||||||
@Input()
|
@Input()
|
||||||
document: Document
|
document: Document
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
displayFields: Set<DocumentDisplayField>
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
dblClickDocument = new EventEmitter()
|
dblClickDocument = new EventEmitter()
|
||||||
|
|
||||||
|
@ -11,8 +11,24 @@
|
|||||||
<button ngbDropdownItem (click)="list.selectAll()" i18n>Select all</button>
|
<button ngbDropdownItem (click)="list.selectAll()" i18n>Select all</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ngbDropdown class="d-flex">
|
||||||
|
<button class="btn btn-sm btn-outline-primary" id="dropdownDisplayFields" ngbDropdownToggle>
|
||||||
|
<i-bs name="card-heading"></i-bs>
|
||||||
|
<div class="d-none d-sm-inline"> <ng-container i18n>Show</ng-container></div>
|
||||||
|
</button>
|
||||||
|
<div ngbDropdownMenu aria-labelledby="dropdownDisplayFields" class="shadow">
|
||||||
|
<div class="px-3">
|
||||||
|
@for (field of settingsService.allDocumentDisplayFields; track field.id) {
|
||||||
|
<div class="form-check small">
|
||||||
|
<input type="checkbox" class="form-check-input" id="displayField{{field.id}}" [checked]="activeDisplayFields.has(field.id)" (change)="toggleDisplayField(field.id)">
|
||||||
|
<label class="form-check-label small" for="displayField{{field.id}}">{{field.name}}</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="btn-group flex-fill" role="group">
|
<div class="btn-group flex-fill" role="group">
|
||||||
<input type="radio" class="btn-check" [(ngModel)]="displayMode" value="details" (ngModelChange)="saveDisplayMode()" id="displayModeDetails" name="displayModeDetails">
|
<input type="radio" class="btn-check" [(ngModel)]="displayMode" value="table" (ngModelChange)="saveDisplayMode()" id="displayModeDetails" name="displayModeDetails">
|
||||||
<label for="displayModeDetails" class="btn btn-outline-primary btn-sm">
|
<label for="displayModeDetails" class="btn btn-outline-primary btn-sm">
|
||||||
<i-bs name="list-ul"></i-bs>
|
<i-bs name="list-ul"></i-bs>
|
||||||
</label>
|
</label>
|
||||||
@ -125,23 +141,35 @@
|
|||||||
@if (displayMode === 'largeCards') {
|
@if (displayMode === 'largeCards') {
|
||||||
<div>
|
<div>
|
||||||
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
||||||
<pngx-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" (dblClickDocument)="openDocumentDetail(d)" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)" (clickStoragePath)="clickStoragePath($event)" (clickMoreLike)="clickMoreLike(d.id)">
|
<pngx-document-card-large
|
||||||
|
[selected]="list.isSelected(d)"
|
||||||
|
(toggleSelected)="toggleSelected(d, $event)"
|
||||||
|
(dblClickDocument)="openDocumentDetail(d)"
|
||||||
|
[document]="d"
|
||||||
|
[displayFields]="activeDisplayFields"
|
||||||
|
(clickTag)="clickTag($event)"
|
||||||
|
(clickCorrespondent)="clickCorrespondent($event)"
|
||||||
|
(clickDocumentType)="clickDocumentType($event)"
|
||||||
|
(clickStoragePath)="clickStoragePath($event)"
|
||||||
|
(clickMoreLike)="clickMoreLike(d.id)">
|
||||||
</pngx-document-card-large>
|
</pngx-document-card-large>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (displayMode === 'details') {
|
@if (displayMode === 'details' || displayMode === 'table') {
|
||||||
<table class="table table-sm align-middle border shadow-sm">
|
<table class="table table-sm align-middle border shadow-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th class="d-none d-lg-table-cell"
|
@if (activeDisplayFields.has(DocumentDisplayField.ASN)) {
|
||||||
pngxSortable="archive_serial_number"
|
<th class="d-none d-lg-table-cell"
|
||||||
title="Sort by ASN" i18n-title
|
pngxSortable="archive_serial_number"
|
||||||
[currentSortField]="list.sortField"
|
title="Sort by ASN" i18n-title
|
||||||
[currentSortReverse]="list.sortReverse"
|
[currentSortField]="list.sortField"
|
||||||
(sort)="onSort($event)"
|
[currentSortReverse]="list.sortReverse"
|
||||||
i18n>ASN</th>
|
(sort)="onSort($event)"
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) {
|
i18n>ASN</th>
|
||||||
|
}
|
||||||
|
@if (activeDisplayFields.has(DocumentDisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) {
|
||||||
<th class="d-none d-md-table-cell"
|
<th class="d-none d-md-table-cell"
|
||||||
pngxSortable="correspondent__name"
|
pngxSortable="correspondent__name"
|
||||||
title="Sort by correspondent" i18n-title
|
title="Sort by correspondent" i18n-title
|
||||||
@ -150,22 +178,26 @@
|
|||||||
(sort)="onSort($event)"
|
(sort)="onSort($event)"
|
||||||
i18n>Correspondent</th>
|
i18n>Correspondent</th>
|
||||||
}
|
}
|
||||||
<th
|
@if (activeDisplayFields.has(DocumentDisplayField.TITLE)) {
|
||||||
pngxSortable="title"
|
<th
|
||||||
title="Sort by title" i18n-title
|
pngxSortable="title"
|
||||||
class="w-40"
|
title="Sort by title" i18n-title
|
||||||
[currentSortField]="list.sortField"
|
class="w-40"
|
||||||
[currentSortReverse]="list.sortReverse"
|
[currentSortField]="list.sortField"
|
||||||
(sort)="onSort($event)"
|
[currentSortReverse]="list.sortReverse"
|
||||||
i18n>Title</th>
|
(sort)="onSort($event)"
|
||||||
<th class="d-none d-xl-table-cell"
|
i18n>Title</th>
|
||||||
pngxSortable="owner"
|
}
|
||||||
title="Sort by owner" i18n-title
|
@if (activeDisplayFields.has(DocumentDisplayField.OWNER)) {
|
||||||
[currentSortField]="list.sortField"
|
<th class="d-none d-xl-table-cell"
|
||||||
[currentSortReverse]="list.sortReverse"
|
pngxSortable="owner"
|
||||||
(sort)="onSort($event)"
|
title="Sort by owner" i18n-title
|
||||||
i18n>Owner</th>
|
[currentSortField]="list.sortField"
|
||||||
@if (notesEnabled) {
|
[currentSortReverse]="list.sortReverse"
|
||||||
|
(sort)="onSort($event)"
|
||||||
|
i18n>Owner</th>
|
||||||
|
}
|
||||||
|
@if (activeDisplayFields.has(DocumentDisplayField.NOTES) && notesEnabled) {
|
||||||
<th class="d-none d-xl-table-cell"
|
<th class="d-none d-xl-table-cell"
|
||||||
pngxSortable="num_notes"
|
pngxSortable="num_notes"
|
||||||
title="Sort by notes" i18n-title
|
title="Sort by notes" i18n-title
|
||||||
@ -174,7 +206,7 @@
|
|||||||
(sort)="onSort($event)"
|
(sort)="onSort($event)"
|
||||||
i18n>Notes</th>
|
i18n>Notes</th>
|
||||||
}
|
}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) {
|
@if (activeDisplayFields.has(DocumentDisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) {
|
||||||
<th class="d-none d-xl-table-cell"
|
<th class="d-none d-xl-table-cell"
|
||||||
pngxSortable="document_type__name"
|
pngxSortable="document_type__name"
|
||||||
title="Sort by document type" i18n-title
|
title="Sort by document type" i18n-title
|
||||||
@ -183,7 +215,7 @@
|
|||||||
(sort)="onSort($event)"
|
(sort)="onSort($event)"
|
||||||
i18n>Document type</th>
|
i18n>Document type</th>
|
||||||
}
|
}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) {
|
@if (activeDisplayFields.has(DocumentDisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) {
|
||||||
<th class="d-none d-xl-table-cell"
|
<th class="d-none d-xl-table-cell"
|
||||||
pngxSortable="storage_path__name"
|
pngxSortable="storage_path__name"
|
||||||
title="Sort by storage path" i18n-title
|
title="Sort by storage path" i18n-title
|
||||||
@ -192,20 +224,24 @@
|
|||||||
(sort)="onSort($event)"
|
(sort)="onSort($event)"
|
||||||
i18n>Storage path</th>
|
i18n>Storage path</th>
|
||||||
}
|
}
|
||||||
<th
|
@if (activeDisplayFields.has(DocumentDisplayField.CREATED)) {
|
||||||
pngxSortable="created"
|
<th
|
||||||
title="Sort by created date" i18n-title
|
pngxSortable="created"
|
||||||
[currentSortField]="list.sortField"
|
title="Sort by created date" i18n-title
|
||||||
[currentSortReverse]="list.sortReverse"
|
[currentSortField]="list.sortField"
|
||||||
(sort)="onSort($event)"
|
[currentSortReverse]="list.sortReverse"
|
||||||
i18n>Created</th>
|
(sort)="onSort($event)"
|
||||||
<th class="d-none d-xl-table-cell"
|
i18n>Created</th>
|
||||||
pngxSortable="added"
|
}
|
||||||
title="Sort by added date" i18n-title
|
@if (activeDisplayFields.has(DocumentDisplayField.ADDED)) {
|
||||||
[currentSortField]="list.sortField"
|
<th class="d-none d-xl-table-cell"
|
||||||
[currentSortReverse]="list.sortReverse"
|
pngxSortable="added"
|
||||||
(sort)="onSort($event)"
|
title="Sort by added date" i18n-title
|
||||||
i18n>Added</th>
|
[currentSortField]="list.sortField"
|
||||||
|
[currentSortReverse]="list.sortReverse"
|
||||||
|
(sort)="onSort($event)"
|
||||||
|
i18n>Added</th>
|
||||||
|
}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
||||||
@ -216,10 +252,12 @@
|
|||||||
<label class="form-check-label" for="docCheck{{d.id}}"></label>
|
<label class="form-check-label" for="docCheck{{d.id}}"></label>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="d-none d-lg-table-cell">
|
@if (activeDisplayFields.has(DocumentDisplayField.ASN)) {
|
||||||
{{d.archive_serial_number}}
|
<td class="d-none d-lg-table-cell">
|
||||||
</td>
|
{{d.archive_serial_number}}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) {
|
</td>
|
||||||
|
}
|
||||||
|
@if (activeDisplayFields.has(DocumentDisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) {
|
||||||
<td class="d-none d-md-table-cell">
|
<td class="d-none d-md-table-cell">
|
||||||
@if (d.correspondent) {
|
@if (d.correspondent) {
|
||||||
<a (click)="clickCorrespondent(d.correspondent);$event.stopPropagation()" title="Filter by correspondent" i18n-title>{{(d.correspondent$ | async)?.name}}</a>
|
<a (click)="clickCorrespondent(d.correspondent);$event.stopPropagation()" title="Filter by correspondent" i18n-title>{{(d.correspondent$ | async)?.name}}</a>
|
||||||
@ -227,15 +265,21 @@
|
|||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
<td>
|
<td>
|
||||||
<a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
|
@if (activeDisplayFields.has(DocumentDisplayField.TITLE)) {
|
||||||
@for (t of d.tags$ | async; track t) {
|
<a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
|
||||||
<pngx-tag [tag]="t" class="ms-1" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(t.id);$event.stopPropagation()"></pngx-tag>
|
}
|
||||||
}
|
@if (activeDisplayFields.has(DocumentDisplayField.TAGS)) {
|
||||||
</td>
|
@for (t of d.tags$ | async; track t) {
|
||||||
<td>
|
<pngx-tag [tag]="t" class="ms-1" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(t.id);$event.stopPropagation()"></pngx-tag>
|
||||||
{{d.owner | username}}
|
}
|
||||||
</td>
|
}
|
||||||
@if (notesEnabled) {
|
</td>
|
||||||
|
@if (activeDisplayFields.has(DocumentDisplayField.OWNER)) {
|
||||||
|
<td>
|
||||||
|
{{d.owner | username}}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
@if (activeDisplayFields.has(DocumentDisplayField.NOTES) && notesEnabled) {
|
||||||
<td class="d-none d-xl-table-cell">
|
<td class="d-none d-xl-table-cell">
|
||||||
@if (d.notes.length) {
|
@if (d.notes.length) {
|
||||||
<a routerLink="/documents/{{d.id}}/notes" class="btn btn-sm p-0">
|
<a routerLink="/documents/{{d.id}}/notes" class="btn btn-sm p-0">
|
||||||
@ -246,26 +290,30 @@
|
|||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) {
|
@if (activeDisplayFields.has(DocumentDisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) {
|
||||||
<td class="d-none d-xl-table-cell">
|
<td class="d-none d-xl-table-cell">
|
||||||
@if (d.document_type) {
|
@if (d.document_type) {
|
||||||
<a (click)="clickDocumentType(d.document_type);$event.stopPropagation()" title="Filter by document type" i18n-title>{{(d.document_type$ | async)?.name}}</a>
|
<a (click)="clickDocumentType(d.document_type);$event.stopPropagation()" title="Filter by document type" i18n-title>{{(d.document_type$ | async)?.name}}</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
@if (permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) {
|
@if (activeDisplayFields.has(DocumentDisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) {
|
||||||
<td class="d-none d-xl-table-cell">
|
<td class="d-none d-xl-table-cell">
|
||||||
@if (d.storage_path) {
|
@if (d.storage_path) {
|
||||||
<a (click)="clickStoragePath(d.storage_path);$event.stopPropagation()" title="Filter by storage path" i18n-title>{{(d.storage_path$ | async)?.name}}</a>
|
<a (click)="clickStoragePath(d.storage_path);$event.stopPropagation()" title="Filter by storage path" i18n-title>{{(d.storage_path$ | async)?.name}}</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
<td>
|
@if (activeDisplayFields.has(DocumentDisplayField.CREATED)) {
|
||||||
{{d.created_date | customDate}}
|
<td>
|
||||||
</td>
|
{{d.created_date | customDate}}
|
||||||
<td class="d-none d-xl-table-cell">
|
</td>
|
||||||
{{d.added | customDate}}
|
}
|
||||||
</td>
|
@if (activeDisplayFields.has(DocumentDisplayField.ADDED)) {
|
||||||
|
<td class="d-none d-xl-table-cell">
|
||||||
|
{{d.added | customDate}}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -274,7 +322,17 @@
|
|||||||
@if (displayMode === 'smallCards') {
|
@if (displayMode === 'smallCards') {
|
||||||
<div class="row row-cols-paperless-cards">
|
<div class="row row-cols-paperless-cards">
|
||||||
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
@for (d of list.documents; track trackByDocumentId($index, d)) {
|
||||||
<pngx-document-card-small class="p-0" [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" (dblClickDocument)="openDocumentDetail(d)" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickStoragePath)="clickStoragePath($event)" (clickDocumentType)="clickDocumentType($event)"></pngx-document-card-small>
|
<pngx-document-card-small class="p-0"
|
||||||
|
[selected]="list.isSelected(d)"
|
||||||
|
(toggleSelected)="toggleSelected(d, $event)"
|
||||||
|
(dblClickDocument)="openDocumentDetail(d)"
|
||||||
|
[document]="d"
|
||||||
|
(clickTag)="clickTag($event)"
|
||||||
|
[displayFields]="activeDisplayFields"
|
||||||
|
(clickCorrespondent)="clickCorrespondent($event)"
|
||||||
|
(clickStoragePath)="clickStoragePath($event)"
|
||||||
|
(clickDocumentType)="clickDocumentType($event)">
|
||||||
|
</pngx-document-card-small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,11 @@ import {
|
|||||||
} from 'src/app/utils/filter-rules'
|
} from 'src/app/utils/filter-rules'
|
||||||
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
|
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
|
||||||
import { Document } from 'src/app/data/document'
|
import { Document } from 'src/app/data/document'
|
||||||
import { DisplayMode, SavedView } from 'src/app/data/saved-view'
|
import {
|
||||||
|
DisplayMode,
|
||||||
|
DocumentDisplayField,
|
||||||
|
SavedView,
|
||||||
|
} from 'src/app/data/saved-view'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import {
|
import {
|
||||||
SortableDirective,
|
SortableDirective,
|
||||||
@ -46,6 +50,8 @@ export class DocumentListComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
|
DocumentDisplayField = DocumentDisplayField
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public list: DocumentListViewService,
|
public list: DocumentListViewService,
|
||||||
public savedViewService: SavedViewService,
|
public savedViewService: SavedViewService,
|
||||||
@ -55,7 +61,7 @@ export class DocumentListComponent
|
|||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private consumerStatusService: ConsumerStatusService,
|
||||||
public openDocumentsService: OpenDocumentsService,
|
public openDocumentsService: OpenDocumentsService,
|
||||||
private settingsService: SettingsService,
|
public settingsService: SettingsService,
|
||||||
public permissionService: PermissionsService
|
public permissionService: PermissionsService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
@ -67,6 +73,18 @@ export class DocumentListComponent
|
|||||||
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
|
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
|
||||||
|
|
||||||
displayMode: string = DisplayMode.SMALL_CARDS // largeCards, smallCards, details
|
displayMode: string = DisplayMode.SMALL_CARDS // largeCards, smallCards, details
|
||||||
|
activeDisplayFields: Set<string> = new Set([
|
||||||
|
DocumentDisplayField.TITLE,
|
||||||
|
DocumentDisplayField.CORRESPONDENT,
|
||||||
|
DocumentDisplayField.CREATED,
|
||||||
|
DocumentDisplayField.TAGS,
|
||||||
|
DocumentDisplayField.DOCUMENT_TYPE,
|
||||||
|
DocumentDisplayField.STORAGE_PATH,
|
||||||
|
DocumentDisplayField.NOTES,
|
||||||
|
DocumentDisplayField.OWNER,
|
||||||
|
DocumentDisplayField.ASN,
|
||||||
|
DocumentDisplayField.SHARED,
|
||||||
|
])
|
||||||
|
|
||||||
unmodifiedFilterRules: FilterRule[] = []
|
unmodifiedFilterRules: FilterRule[] = []
|
||||||
private unmodifiedSavedView: SavedView
|
private unmodifiedSavedView: SavedView
|
||||||
@ -131,11 +149,33 @@ export class DocumentListComponent
|
|||||||
localStorage.setItem('document-list:displayMode', this.displayMode)
|
localStorage.setItem('document-list:displayMode', this.displayMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveDisplayFields() {
|
||||||
|
localStorage.setItem(
|
||||||
|
'document-list:displayFields',
|
||||||
|
JSON.stringify(Array.from(this.activeDisplayFields))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDisplayField(field: string) {
|
||||||
|
if (this.activeDisplayFields.has(field)) {
|
||||||
|
this.activeDisplayFields.delete(field)
|
||||||
|
} else {
|
||||||
|
this.activeDisplayFields.add(field)
|
||||||
|
}
|
||||||
|
this.saveDisplayFields()
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (localStorage.getItem('document-list:displayMode') != null) {
|
if (localStorage.getItem('document-list:displayMode') != null) {
|
||||||
this.displayMode = localStorage.getItem('document-list:displayMode')
|
this.displayMode = localStorage.getItem('document-list:displayMode')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (localStorage.getItem('document-list:displayFields') != null) {
|
||||||
|
this.activeDisplayFields = new Set(
|
||||||
|
JSON.parse(localStorage.getItem('document-list:displayFields'))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
this.consumerStatusService
|
this.consumerStatusService
|
||||||
.onDocumentConsumptionFinished()
|
.onDocumentConsumptionFinished()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
|
@ -15,6 +15,10 @@ export enum DocumentDisplayField {
|
|||||||
DOCUMENT_TYPE = 'documenttype',
|
DOCUMENT_TYPE = 'documenttype',
|
||||||
STORAGE_PATH = 'storagepath',
|
STORAGE_PATH = 'storagepath',
|
||||||
CUSTOM_FIELD = 'custom_field_',
|
CUSTOM_FIELD = 'custom_field_',
|
||||||
|
NOTES = 'note',
|
||||||
|
OWNER = 'owner',
|
||||||
|
SHARED = 'shared',
|
||||||
|
ASN = 'asn',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DOCUMENT_DISPLAY_FIELDS = [
|
export const DOCUMENT_DISPLAY_FIELDS = [
|
||||||
@ -46,6 +50,22 @@ export const DOCUMENT_DISPLAY_FIELDS = [
|
|||||||
id: DocumentDisplayField.STORAGE_PATH,
|
id: DocumentDisplayField.STORAGE_PATH,
|
||||||
name: $localize`Storage path`,
|
name: $localize`Storage path`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: DocumentDisplayField.NOTES,
|
||||||
|
name: $localize`Notes`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: DocumentDisplayField.OWNER,
|
||||||
|
name: $localize`Owner`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: DocumentDisplayField.SHARED,
|
||||||
|
name: $localize`Shared`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: DocumentDisplayField.ASN,
|
||||||
|
name: $localize`ASN`,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export interface SavedView extends ObjectWithPermissions {
|
export interface SavedView extends ObjectWithPermissions {
|
||||||
|
@ -19,9 +19,18 @@ import {
|
|||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import { UiSettings, SETTINGS, SETTINGS_KEYS } from '../data/ui-settings'
|
import { UiSettings, SETTINGS, SETTINGS_KEYS } from '../data/ui-settings'
|
||||||
import { User } from '../data/user'
|
import { User } from '../data/user'
|
||||||
import { PermissionsService } from './permissions.service'
|
import {
|
||||||
|
PermissionAction,
|
||||||
|
PermissionType,
|
||||||
|
PermissionsService,
|
||||||
|
} from './permissions.service'
|
||||||
import { ToastService } from './toast.service'
|
import { ToastService } from './toast.service'
|
||||||
import { SavedView } from '../data/saved-view'
|
import {
|
||||||
|
DOCUMENT_DISPLAY_FIELDS,
|
||||||
|
DocumentDisplayField,
|
||||||
|
SavedView,
|
||||||
|
} from '../data/saved-view'
|
||||||
|
import { CustomFieldsService } from './rest/custom-fields.service'
|
||||||
|
|
||||||
export interface LanguageOption {
|
export interface LanguageOption {
|
||||||
code: string
|
code: string
|
||||||
@ -257,6 +266,8 @@ export class SettingsService {
|
|||||||
public globalDropzoneActive: boolean = false
|
public globalDropzoneActive: boolean = false
|
||||||
public organizingSidebarSavedViews: boolean = false
|
public organizingSidebarSavedViews: boolean = false
|
||||||
|
|
||||||
|
public allDocumentDisplayFields: any
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
rendererFactory: RendererFactory2,
|
rendererFactory: RendererFactory2,
|
||||||
@Inject(DOCUMENT) private document,
|
@Inject(DOCUMENT) private document,
|
||||||
@ -265,7 +276,8 @@ export class SettingsService {
|
|||||||
@Inject(LOCALE_ID) private localeId: string,
|
@Inject(LOCALE_ID) private localeId: string,
|
||||||
protected http: HttpClient,
|
protected http: HttpClient,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private permissionsService: PermissionsService
|
private permissionsService: PermissionsService,
|
||||||
|
private customFieldsService: CustomFieldsService
|
||||||
) {
|
) {
|
||||||
this._renderer = rendererFactory.createRenderer(null, null)
|
this._renderer = rendererFactory.createRenderer(null, null)
|
||||||
}
|
}
|
||||||
@ -288,6 +300,26 @@ export class SettingsService {
|
|||||||
uisettings.permissions,
|
uisettings.permissions,
|
||||||
this.currentUser
|
this.currentUser
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this.allDocumentDisplayFields = DOCUMENT_DISPLAY_FIELDS
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.permissionsService.currentUserCan(
|
||||||
|
PermissionAction.View,
|
||||||
|
PermissionType.CustomField
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.customFieldsService.listAll().subscribe((r) => {
|
||||||
|
this.allDocumentDisplayFields = DOCUMENT_DISPLAY_FIELDS.concat(
|
||||||
|
r.results.map((field) => {
|
||||||
|
return {
|
||||||
|
id: `${DocumentDisplayField.CUSTOM_FIELD}${field.id}` as any,
|
||||||
|
name: field.name,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user