mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-02-07 23:42:46 -06:00
Ok lets just merge it all together
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
<pngx-page-header
|
||||
title="Attributes"
|
||||
i18n-title
|
||||
info="Manage tags, correspondents, document types, storage paths, and custom fields."
|
||||
i18n-info
|
||||
[subTitle]="activeTabLabel"
|
||||
[loading]="activeHeaderLoading"
|
||||
>
|
||||
@if (activeBulkList) {
|
||||
<div ngbDropdown class="btn-group flex-fill d-sm-none">
|
||||
<button class="btn btn-sm btn-outline-primary" id="dropdownSelectMobile" ngbDropdownToggle>
|
||||
<i-bs name="text-indent-left"></i-bs>
|
||||
<div class="d-none d-sm-inline"> <ng-container i18n>Select</ng-container></div>
|
||||
@if (activeBulkList.selectedObjects.size > 0) {
|
||||
<pngx-clearable-badge [selected]="activeBulkList.selectedObjects.size > 0" [number]="activeBulkList.selectedObjects.size" (cleared)="activeBulkList.selectNone()"></pngx-clearable-badge><span class="visually-hidden">selected</span>
|
||||
}
|
||||
</button>
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownSelectMobile" class="shadow">
|
||||
<button ngbDropdownItem (click)="activeBulkList.selectNone()" i18n>Select none</button>
|
||||
<button ngbDropdownItem (click)="activeBulkList.selectPage(true)" i18n>Select page</button>
|
||||
<button ngbDropdownItem (click)="activeBulkList.selectAll()" i18n>Select all</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-none d-sm-flex flex-fill me-3">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text border-0" i18n>Select:</span>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm flex-nowrap">
|
||||
@if (activeBulkList.selectedObjects.size > 0) {
|
||||
<button class="btn btn-sm btn-outline-secondary" (click)="activeBulkList.selectNone()">
|
||||
<i-bs name="slash-circle"></i-bs> <ng-container i18n>None</ng-container>
|
||||
</button>
|
||||
}
|
||||
<button class="btn btn-sm btn-outline-primary" (click)="activeBulkList.selectPage(true)">
|
||||
<i-bs name="file-earmark-check"></i-bs> <ng-container i18n>Page</ng-container>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" (click)="activeBulkList.selectAll()">
|
||||
<i-bs name="check-all"></i-bs> <ng-container i18n>All</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="activeBulkList.setPermissions()"
|
||||
[disabled]="!activeBulkList.userCanBulkEdit(PermissionAction.Change) || activeBulkList.selectedObjects.size === 0">
|
||||
<i-bs name="person-fill-lock"></i-bs> <ng-container i18n>Permissions</ng-container>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" (click)="activeBulkList.delete()"
|
||||
[disabled]="!activeBulkList.userCanBulkEdit(PermissionAction.Delete) || activeBulkList.selectedObjects.size === 0">
|
||||
<i-bs name="trash"></i-bs> <ng-container i18n>Delete</ng-container>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary ms-md-5" (click)="activeBulkList.openCreateDialog()"
|
||||
*pngxIfPermissions="{ action: PermissionAction.Add, type: activeBulkList.permissionType }">
|
||||
<i-bs name="plus-circle"></i-bs> <ng-container i18n>Create</ng-container>
|
||||
</button>
|
||||
} @else if (activeCustomFields) {
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="activeCustomFields.editField()"
|
||||
*pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.CustomField }">
|
||||
<i-bs name="plus-circle"></i-bs> <ng-container i18n>Add Field</ng-container>
|
||||
</button>
|
||||
}
|
||||
</pngx-page-header>
|
||||
|
||||
<ul ngbNav #nav="ngbNav" (navChange)="onNavChange($event)" [(activeId)]="activeNavID" class="nav-tabs">
|
||||
@if (canViewTags) {
|
||||
<li [ngbNavItem]="DocumentAttributesNavIDs.Tags">
|
||||
<a ngbNavLink>
|
||||
<ng-container i18n>Tags</ng-container>
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<pngx-tag-list></pngx-tag-list>
|
||||
</ng-template>
|
||||
</li>
|
||||
}
|
||||
@if (canViewCorrespondents) {
|
||||
<li [ngbNavItem]="DocumentAttributesNavIDs.Correspondents">
|
||||
<a ngbNavLink>
|
||||
</i-bs><ng-container i18n>Correspondents</ng-container>
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<pngx-correspondent-list></pngx-correspondent-list>
|
||||
</ng-template>
|
||||
</li>
|
||||
}
|
||||
@if (canViewDocumentTypes) {
|
||||
<li [ngbNavItem]="DocumentAttributesNavIDs.DocumentTypes">
|
||||
<a ngbNavLink>
|
||||
</i-bs><ng-container i18n>Document types</ng-container>
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<pngx-document-type-list></pngx-document-type-list>
|
||||
</ng-template>
|
||||
</li>
|
||||
}
|
||||
@if (canViewStoragePaths) {
|
||||
<li [ngbNavItem]="DocumentAttributesNavIDs.StoragePaths">
|
||||
<a ngbNavLink>
|
||||
</i-bs><ng-container i18n>Storage paths</ng-container>
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<pngx-storage-path-list></pngx-storage-path-list>
|
||||
</ng-template>
|
||||
</li>
|
||||
}
|
||||
@if (canViewCustomFields) {
|
||||
<li [ngbNavItem]="DocumentAttributesNavIDs.CustomFields">
|
||||
<a ngbNavLink>
|
||||
</i-bs><ng-container i18n>Custom fields</ng-container>
|
||||
</a>
|
||||
<ng-template ngbNavContent>
|
||||
<pngx-custom-fields></pngx-custom-fields>
|
||||
</ng-template>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
|
||||
@@ -0,0 +1,237 @@
|
||||
import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import {
|
||||
NgbDropdownModule,
|
||||
NgbNavChangeEvent,
|
||||
NgbNavModule,
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Subject, takeUntil } from 'rxjs'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import {
|
||||
PermissionAction,
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { ClearableBadgeComponent } from '../../common/clearable-badge/clearable-badge.component'
|
||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||
import { CorrespondentListComponent } from '../correspondent-list/correspondent-list.component'
|
||||
import { CustomFieldsComponent } from '../custom-fields/custom-fields.component'
|
||||
import { DocumentTypeListComponent } from '../document-type-list/document-type-list.component'
|
||||
import { ManagementListComponent } from '../management-list/management-list.component'
|
||||
import { StoragePathListComponent } from '../storage-path-list/storage-path-list.component'
|
||||
import { TagListComponent } from '../tag-list/tag-list.component'
|
||||
|
||||
enum DocumentAttributesNavIDs {
|
||||
Tags = 1,
|
||||
Correspondents = 2,
|
||||
DocumentTypes = 3,
|
||||
StoragePaths = 4,
|
||||
CustomFields = 5,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-document-attributes',
|
||||
templateUrl: './document-attributes.component.html',
|
||||
styleUrls: ['./document-attributes.component.scss'],
|
||||
imports: [
|
||||
PageHeaderComponent,
|
||||
NgbNavModule,
|
||||
NgbDropdownModule,
|
||||
NgxBootstrapIconsModule,
|
||||
IfPermissionsDirective,
|
||||
ClearableBadgeComponent,
|
||||
TagListComponent,
|
||||
CorrespondentListComponent,
|
||||
DocumentTypeListComponent,
|
||||
StoragePathListComponent,
|
||||
CustomFieldsComponent,
|
||||
],
|
||||
})
|
||||
export class DocumentAttributesComponent implements OnInit, OnDestroy {
|
||||
private readonly permissionsService = inject(PermissionsService)
|
||||
private readonly activatedRoute = inject(ActivatedRoute)
|
||||
private readonly router = inject(Router)
|
||||
private readonly unsubscribeNotifier = new Subject<void>()
|
||||
|
||||
protected readonly DocumentAttributesNavIDs = DocumentAttributesNavIDs
|
||||
protected readonly PermissionAction = PermissionAction
|
||||
protected readonly PermissionType = PermissionType
|
||||
|
||||
@ViewChild(TagListComponent) private tagList?: TagListComponent
|
||||
@ViewChild(CorrespondentListComponent)
|
||||
private correspondentList?: CorrespondentListComponent
|
||||
@ViewChild(DocumentTypeListComponent)
|
||||
private documentTypeList?: DocumentTypeListComponent
|
||||
@ViewChild(StoragePathListComponent)
|
||||
private storagePathList?: StoragePathListComponent
|
||||
@ViewChild(CustomFieldsComponent)
|
||||
private customFields?: CustomFieldsComponent
|
||||
|
||||
activeNavID: number = null
|
||||
|
||||
get activeBulkList(): ManagementListComponent<any> | null {
|
||||
switch (this.activeNavID) {
|
||||
case DocumentAttributesNavIDs.Tags:
|
||||
return this.tagList ?? null
|
||||
case DocumentAttributesNavIDs.Correspondents:
|
||||
return this.correspondentList ?? null
|
||||
case DocumentAttributesNavIDs.DocumentTypes:
|
||||
return this.documentTypeList ?? null
|
||||
case DocumentAttributesNavIDs.StoragePaths:
|
||||
return this.storagePathList ?? null
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
get activeCustomFields(): CustomFieldsComponent | null {
|
||||
return this.activeNavID === DocumentAttributesNavIDs.CustomFields
|
||||
? (this.customFields ?? null)
|
||||
: null
|
||||
}
|
||||
|
||||
get activeTabLabel(): string {
|
||||
switch (this.activeNavID) {
|
||||
case DocumentAttributesNavIDs.Tags:
|
||||
return $localize`Tags`
|
||||
case DocumentAttributesNavIDs.Correspondents:
|
||||
return $localize`Correspondents`
|
||||
case DocumentAttributesNavIDs.DocumentTypes:
|
||||
return $localize`Document types`
|
||||
case DocumentAttributesNavIDs.StoragePaths:
|
||||
return $localize`Storage paths`
|
||||
case DocumentAttributesNavIDs.CustomFields:
|
||||
return $localize`Custom fields`
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
get activeHeaderLoading(): boolean {
|
||||
return (
|
||||
this.activeBulkList?.loading ?? this.activeCustomFields?.loading ?? false
|
||||
)
|
||||
}
|
||||
|
||||
get canViewTags(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Tag
|
||||
)
|
||||
}
|
||||
|
||||
get canViewCorrespondents(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Correspondent
|
||||
)
|
||||
}
|
||||
|
||||
get canViewDocumentTypes(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.DocumentType
|
||||
)
|
||||
}
|
||||
|
||||
get canViewStoragePaths(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.StoragePath
|
||||
)
|
||||
}
|
||||
|
||||
get canViewCustomFields(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.CustomField
|
||||
)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.paramMap
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe((paramMap) => {
|
||||
const section = paramMap.get('section')
|
||||
const navIDFromSection =
|
||||
this.getNavIDForSection(section) ?? this.getDefaultNavID()
|
||||
|
||||
if (navIDFromSection == null) {
|
||||
this.router.navigate(['/dashboard'], { replaceUrl: true })
|
||||
return
|
||||
}
|
||||
|
||||
if (this.activeNavID !== navIDFromSection) {
|
||||
this.activeNavID = navIDFromSection
|
||||
}
|
||||
|
||||
if (!section || this.getNavIDForSection(section) == null) {
|
||||
this.router.navigate(
|
||||
['attributes', this.getSectionForNavID(this.activeNavID)],
|
||||
{ replaceUrl: true }
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.unsubscribeNotifier.next()
|
||||
this.unsubscribeNotifier.complete()
|
||||
}
|
||||
|
||||
onNavChange(navChangeEvent: NgbNavChangeEvent): void {
|
||||
const nextSection = this.getSectionForNavID(navChangeEvent.nextId)
|
||||
if (!nextSection) {
|
||||
return
|
||||
}
|
||||
this.router.navigate(['attributes', nextSection])
|
||||
}
|
||||
|
||||
private getDefaultNavID(): DocumentAttributesNavIDs | null {
|
||||
if (this.canViewTags) return DocumentAttributesNavIDs.Tags
|
||||
if (this.canViewCorrespondents)
|
||||
return DocumentAttributesNavIDs.Correspondents
|
||||
if (this.canViewDocumentTypes) return DocumentAttributesNavIDs.DocumentTypes
|
||||
if (this.canViewStoragePaths) return DocumentAttributesNavIDs.StoragePaths
|
||||
if (this.canViewCustomFields) return DocumentAttributesNavIDs.CustomFields
|
||||
return null
|
||||
}
|
||||
|
||||
private getNavIDForSection(section: string): DocumentAttributesNavIDs | null {
|
||||
if (!section) return null
|
||||
const navIDKey: string = Object.keys(DocumentAttributesNavIDs).find(
|
||||
(navID) => navID.toLowerCase() === section.toLowerCase()
|
||||
)
|
||||
if (!navIDKey) return null
|
||||
|
||||
const navID = DocumentAttributesNavIDs[navIDKey]
|
||||
if (!this.isNavIDAllowed(navID)) return null
|
||||
return navID
|
||||
}
|
||||
|
||||
private getSectionForNavID(navID: number): string | null {
|
||||
if (!this.isNavIDAllowed(navID)) return null
|
||||
const [foundNavIDKey] = Object.entries(DocumentAttributesNavIDs).find(
|
||||
([, navIDValue]) => navIDValue === navID
|
||||
)
|
||||
return foundNavIDKey?.toLowerCase() ?? null
|
||||
}
|
||||
|
||||
private isNavIDAllowed(navID: number): boolean {
|
||||
switch (navID) {
|
||||
case DocumentAttributesNavIDs.Tags:
|
||||
return this.canViewTags
|
||||
case DocumentAttributesNavIDs.Correspondents:
|
||||
return this.canViewCorrespondents
|
||||
case DocumentAttributesNavIDs.DocumentTypes:
|
||||
return this.canViewDocumentTypes
|
||||
case DocumentAttributesNavIDs.StoragePaths:
|
||||
return this.canViewStoragePaths
|
||||
case DocumentAttributesNavIDs.CustomFields:
|
||||
return this.canViewCustomFields
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user