mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Make frontend list a generic management list
This commit is contained in:
		@@ -12,7 +12,7 @@ import { DocumentAsnComponent } from './components/document-asn/document-asn.com
 | 
			
		||||
import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
 | 
			
		||||
import { DocumentListComponent } from './components/document-list/document-list.component'
 | 
			
		||||
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component'
 | 
			
		||||
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
 | 
			
		||||
import { CustomFieldsListComponent } from './components/manage/custom-fields-list/custom-fields-list.component'
 | 
			
		||||
import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component'
 | 
			
		||||
import { MailComponent } from './components/manage/mail/mail.component'
 | 
			
		||||
import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component'
 | 
			
		||||
@@ -239,7 +239,7 @@ export const routes: Routes = [
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'customfields',
 | 
			
		||||
        component: CustomFieldsComponent,
 | 
			
		||||
        component: CustomFieldsListComponent,
 | 
			
		||||
        canActivate: [PermissionsGuard],
 | 
			
		||||
        data: {
 | 
			
		||||
          requiredPermission: {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
    <pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
 | 
			
		||||
    <pngx-input-select i18n-title title="Data type" [items]="getDataTypes()" formControlName="data_type"></pngx-input-select>
 | 
			
		||||
    @if (typeFieldDisabled) {
 | 
			
		||||
      <small class="d-block mt-n2" i18n>Data type cannot be changed after a field is created</small>
 | 
			
		||||
      <small class="d-block mt-n2 fst-italic text-muted" i18n>Data type cannot be changed after a field is created</small>
 | 
			
		||||
    }
 | 
			
		||||
    <div [formGroup]="objectForm.controls.extra_data">
 | 
			
		||||
      @switch (objectForm.get('data_type').value) {
 | 
			
		||||
@@ -39,6 +39,14 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    </div>
 | 
			
		||||
    <hr/>
 | 
			
		||||
    <pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
 | 
			
		||||
    @if (patternRequired) {
 | 
			
		||||
      <pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
 | 
			
		||||
    }
 | 
			
		||||
    @if (patternRequired) {
 | 
			
		||||
      <pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
 | 
			
		||||
    }
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="modal-footer">
 | 
			
		||||
    <button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ import {
 | 
			
		||||
  CustomFieldDataType,
 | 
			
		||||
  DATA_TYPE_LABELS,
 | 
			
		||||
} from 'src/app/data/custom-field'
 | 
			
		||||
import { MATCH_NONE } from 'src/app/data/matching-model'
 | 
			
		||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
 | 
			
		||||
import { UserService } from 'src/app/services/rest/user.service'
 | 
			
		||||
import { SettingsService } from 'src/app/services/settings.service'
 | 
			
		||||
@@ -107,6 +108,9 @@ export class CustomFieldEditDialogComponent
 | 
			
		||||
        select_options: new FormArray([]),
 | 
			
		||||
        default_currency: new FormControl(null),
 | 
			
		||||
      }),
 | 
			
		||||
      matching_algorithm: new FormControl(MATCH_NONE),
 | 
			
		||||
      match: new FormControl(''),
 | 
			
		||||
      is_insensitive: new FormControl(true),
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { CustomField, DATA_TYPE_LABELS } from 'src/app/data/custom-field'
 | 
			
		||||
import {
 | 
			
		||||
  CustomFieldQueryLogicalOperator,
 | 
			
		||||
  CustomFieldQueryOperator,
 | 
			
		||||
} from 'src/app/data/custom-field-query'
 | 
			
		||||
import { FILTER_CUSTOM_FIELDS_QUERY } from 'src/app/data/filter-rule-type'
 | 
			
		||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
			
		||||
import {
 | 
			
		||||
  PermissionsService,
 | 
			
		||||
  PermissionType,
 | 
			
		||||
} from 'src/app/services/permissions.service'
 | 
			
		||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
 | 
			
		||||
import { ToastService } from 'src/app/services/toast.service'
 | 
			
		||||
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
 | 
			
		||||
import { ManagementListComponent } from '../management-list/management-list.component'
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'pngx-custom-fields-list',
 | 
			
		||||
  templateUrl: './../management-list/management-list.component.html',
 | 
			
		||||
  styleUrls: ['./../management-list/management-list.component.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class CustomFieldsListComponent extends ManagementListComponent<CustomField> {
 | 
			
		||||
  permissionsDisabled = true
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    customFieldsService: CustomFieldsService,
 | 
			
		||||
    modalService: NgbModal,
 | 
			
		||||
    toastService: ToastService,
 | 
			
		||||
    documentListViewService: DocumentListViewService,
 | 
			
		||||
    permissionsService: PermissionsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(
 | 
			
		||||
      customFieldsService,
 | 
			
		||||
      modalService,
 | 
			
		||||
      CustomFieldEditDialogComponent,
 | 
			
		||||
      toastService,
 | 
			
		||||
      documentListViewService,
 | 
			
		||||
      permissionsService,
 | 
			
		||||
      0, // see filterDocuments override below
 | 
			
		||||
      $localize`custom field`,
 | 
			
		||||
      $localize`custom fields`,
 | 
			
		||||
      PermissionType.CustomField,
 | 
			
		||||
      [
 | 
			
		||||
        {
 | 
			
		||||
          key: 'data_type',
 | 
			
		||||
          name: $localize`Data Type`,
 | 
			
		||||
          valueFn: (field: CustomField) => {
 | 
			
		||||
            return DATA_TYPE_LABELS.find((l) => l.id === field.data_type).name
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ]
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  filterDocuments(field: CustomField) {
 | 
			
		||||
    this.documentListViewService.quickFilter([
 | 
			
		||||
      {
 | 
			
		||||
        rule_type: FILTER_CUSTOM_FIELDS_QUERY,
 | 
			
		||||
        value: JSON.stringify([
 | 
			
		||||
          CustomFieldQueryLogicalOperator.Or,
 | 
			
		||||
          [[field.id, CustomFieldQueryOperator.Exists, true]],
 | 
			
		||||
        ]),
 | 
			
		||||
      },
 | 
			
		||||
    ])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getDeleteMessage(object: CustomField) {
 | 
			
		||||
    return $localize`Do you really want to delete the field "${object.name}"?`
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -28,7 +28,7 @@ import { ToastService } from 'src/app/services/toast.service'
 | 
			
		||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
 | 
			
		||||
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
 | 
			
		||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
 | 
			
		||||
import { CustomFieldsComponent } from './custom-fields.component'
 | 
			
		||||
import { CustomFieldsComponent } from './custom-fields-list.component'
 | 
			
		||||
 | 
			
		||||
const fields: CustomField[] = [
 | 
			
		||||
  {
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
<pngx-page-header
 | 
			
		||||
  title="Custom Fields"
 | 
			
		||||
  i18n-title
 | 
			
		||||
  info="Customize the data fields that can be attached to documents."
 | 
			
		||||
  i18n-info
 | 
			
		||||
  infoLink="usage/#custom-fields"
 | 
			
		||||
  >
 | 
			
		||||
  <button type="button" class="btn btn-sm btn-outline-primary" (click)="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 class="list-group">
 | 
			
		||||
 | 
			
		||||
  <li class="list-group-item">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col" i18n>Name</div>
 | 
			
		||||
      <div class="col" i18n>Data Type</div>
 | 
			
		||||
      <div class="col" i18n>Actions</div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </li>
 | 
			
		||||
 | 
			
		||||
  @if (loading) {
 | 
			
		||||
    <li class="list-group-item">
 | 
			
		||||
      <div class="spinner-border spinner-border-sm me-2" role="status"></div>
 | 
			
		||||
      <ng-container i18n>Loading...</ng-container>
 | 
			
		||||
    </li>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @for (field of fields; track field) {
 | 
			
		||||
    <li class="list-group-item">
 | 
			
		||||
      <div class="row fade" [class.show]="show">
 | 
			
		||||
        <div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editField(field)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.CustomField)">{{field.name}}</button></div>
 | 
			
		||||
        <div class="col d-flex align-items-center">{{getDataType(field)}}</div>
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <div class="btn-group d-block d-sm-none">
 | 
			
		||||
            <div ngbDropdown container="body" class="d-inline-block">
 | 
			
		||||
              <button type="button" class="btn btn-link" id="actionsMenuMobile" (click)="$event.stopPropagation()" ngbDropdownToggle>
 | 
			
		||||
                <i-bs name="three-dots-vertical"></i-bs>
 | 
			
		||||
              </button>
 | 
			
		||||
              <div ngbDropdownMenu aria-labelledby="actionsMenuMobile">
 | 
			
		||||
                <button (click)="editField(field)" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.CustomField }" ngbDropdownItem i18n>Edit</button>
 | 
			
		||||
                <button class="text-danger" (click)="deleteField(field)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.CustomField }" ngbDropdownItem i18n>Delete</button>
 | 
			
		||||
                @if (field.document_count > 0) {
 | 
			
		||||
                  <button (click)="filterDocuments(field)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }" ngbDropdownItem i18n>Filter Documents ({{ field.document_count }})</button>
 | 
			
		||||
                }
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="btn-group d-none d-sm-inline-block">
 | 
			
		||||
            <button *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.CustomField }" class="btn btn-sm btn-outline-secondary" type="button" (click)="editField(field)">
 | 
			
		||||
              <i-bs width="1em" height="1em" name="pencil"></i-bs> <ng-container i18n>Edit</ng-container>
 | 
			
		||||
            </button>
 | 
			
		||||
            <button *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.CustomField }" class="btn btn-sm btn-outline-danger" type="button" (click)="deleteField(field)">
 | 
			
		||||
              <i-bs width="1em" height="1em" name="trash"></i-bs> <ng-container i18n>Delete</ng-container>
 | 
			
		||||
            </button>
 | 
			
		||||
          </div>
 | 
			
		||||
          @if (field.document_count > 0) {
 | 
			
		||||
            <div class="btn-group d-none d-sm-inline-block ms-2">
 | 
			
		||||
              <button class="btn btn-sm btn-outline-secondary" type="button" (click)="filterDocuments(field)">
 | 
			
		||||
                <i-bs width="1em" height="1em" name="filter"></i-bs> <ng-container i18n>Documents</ng-container><span class="badge bg-light text-secondary ms-2">{{ field.document_count }}</span>
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          }
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </li>
 | 
			
		||||
  }
 | 
			
		||||
  @if (!loading && fields.length === 0) {
 | 
			
		||||
    <li class="list-group-item" i18n>No fields defined.</li>
 | 
			
		||||
  }
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
// hide caret on mobile dropdown
 | 
			
		||||
.d-block.d-sm-none .dropdown-toggle::after {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,148 +0,0 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  NgbDropdownModule,
 | 
			
		||||
  NgbModal,
 | 
			
		||||
  NgbPaginationModule,
 | 
			
		||||
} from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { delay, takeUntil, tap } from 'rxjs'
 | 
			
		||||
import { CustomField, DATA_TYPE_LABELS } from 'src/app/data/custom-field'
 | 
			
		||||
import {
 | 
			
		||||
  CustomFieldQueryLogicalOperator,
 | 
			
		||||
  CustomFieldQueryOperator,
 | 
			
		||||
} from 'src/app/data/custom-field-query'
 | 
			
		||||
import { FILTER_CUSTOM_FIELDS_QUERY } from 'src/app/data/filter-rule-type'
 | 
			
		||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
 | 
			
		||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
 | 
			
		||||
import { PermissionsService } from 'src/app/services/permissions.service'
 | 
			
		||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
 | 
			
		||||
import { DocumentService } from 'src/app/services/rest/document.service'
 | 
			
		||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
 | 
			
		||||
import { SettingsService } from 'src/app/services/settings.service'
 | 
			
		||||
import { ToastService } from 'src/app/services/toast.service'
 | 
			
		||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
 | 
			
		||||
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
 | 
			
		||||
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
 | 
			
		||||
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'pngx-custom-fields',
 | 
			
		||||
  templateUrl: './custom-fields.component.html',
 | 
			
		||||
  styleUrls: ['./custom-fields.component.scss'],
 | 
			
		||||
  imports: [
 | 
			
		||||
    PageHeaderComponent,
 | 
			
		||||
    IfPermissionsDirective,
 | 
			
		||||
    NgbDropdownModule,
 | 
			
		||||
    NgbPaginationModule,
 | 
			
		||||
    NgxBootstrapIconsModule,
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class CustomFieldsComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  public fields: CustomField[] = []
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private customFieldsService: CustomFieldsService,
 | 
			
		||||
    public permissionsService: PermissionsService,
 | 
			
		||||
    private modalService: NgbModal,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private documentListViewService: DocumentListViewService,
 | 
			
		||||
    private settingsService: SettingsService,
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    private savedViewService: SavedViewService
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.reload()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reload() {
 | 
			
		||||
    this.customFieldsService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(
 | 
			
		||||
        takeUntil(this.unsubscribeNotifier),
 | 
			
		||||
        tap((r) => {
 | 
			
		||||
          this.fields = r.results
 | 
			
		||||
        }),
 | 
			
		||||
        delay(100)
 | 
			
		||||
      )
 | 
			
		||||
      .subscribe(() => {
 | 
			
		||||
        this.show = true
 | 
			
		||||
        this.loading = false
 | 
			
		||||
      })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  editField(field: CustomField) {
 | 
			
		||||
    const modal = this.modalService.open(CustomFieldEditDialogComponent)
 | 
			
		||||
    modal.componentInstance.dialogMode = field
 | 
			
		||||
      ? EditDialogMode.EDIT
 | 
			
		||||
      : EditDialogMode.CREATE
 | 
			
		||||
    modal.componentInstance.object = field
 | 
			
		||||
    modal.componentInstance.succeeded
 | 
			
		||||
      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
			
		||||
      .subscribe((newField) => {
 | 
			
		||||
        this.toastService.showInfo($localize`Saved field "${newField.name}".`)
 | 
			
		||||
        this.customFieldsService.clearCache()
 | 
			
		||||
        this.settingsService.initializeDisplayFields()
 | 
			
		||||
        this.documentService.reload()
 | 
			
		||||
        this.reload()
 | 
			
		||||
      })
 | 
			
		||||
    modal.componentInstance.failed
 | 
			
		||||
      .pipe(takeUntil(this.unsubscribeNotifier))
 | 
			
		||||
      .subscribe((e) => {
 | 
			
		||||
        this.toastService.showError($localize`Error saving field.`, e)
 | 
			
		||||
      })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deleteField(field: CustomField) {
 | 
			
		||||
    const modal = this.modalService.open(ConfirmDialogComponent, {
 | 
			
		||||
      backdrop: 'static',
 | 
			
		||||
    })
 | 
			
		||||
    modal.componentInstance.title = $localize`Confirm delete field`
 | 
			
		||||
    modal.componentInstance.messageBold = $localize`This operation will permanently delete this field.`
 | 
			
		||||
    modal.componentInstance.message = $localize`This operation cannot be undone.`
 | 
			
		||||
    modal.componentInstance.btnClass = 'btn-danger'
 | 
			
		||||
    modal.componentInstance.btnCaption = $localize`Proceed`
 | 
			
		||||
    modal.componentInstance.confirmClicked.subscribe(() => {
 | 
			
		||||
      modal.componentInstance.buttonsEnabled = false
 | 
			
		||||
      this.customFieldsService.delete(field).subscribe({
 | 
			
		||||
        next: () => {
 | 
			
		||||
          modal.close()
 | 
			
		||||
          this.toastService.showInfo($localize`Deleted field "${field.name}"`)
 | 
			
		||||
          this.customFieldsService.clearCache()
 | 
			
		||||
          this.settingsService.initializeDisplayFields()
 | 
			
		||||
          this.documentService.reload()
 | 
			
		||||
          this.savedViewService.reload()
 | 
			
		||||
          this.reload()
 | 
			
		||||
        },
 | 
			
		||||
        error: (e) => {
 | 
			
		||||
          this.toastService.showError(
 | 
			
		||||
            $localize`Error deleting field "${field.name}".`,
 | 
			
		||||
            e
 | 
			
		||||
          )
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getDataType(field: CustomField): string {
 | 
			
		||||
    return DATA_TYPE_LABELS.find((l) => l.id === field.data_type).name
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  filterDocuments(field: CustomField) {
 | 
			
		||||
    this.documentListViewService.quickFilter([
 | 
			
		||||
      {
 | 
			
		||||
        rule_type: FILTER_CUSTOM_FIELDS_QUERY,
 | 
			
		||||
        value: JSON.stringify([
 | 
			
		||||
          CustomFieldQueryLogicalOperator.Or,
 | 
			
		||||
          [[field.id, CustomFieldQueryOperator.Exists, true]],
 | 
			
		||||
        ]),
 | 
			
		||||
      },
 | 
			
		||||
    ])
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  <button class="btn btn-sm btn-outline-secondary" (click)="clearSelection()" [hidden]="selectedObjects.size === 0">
 | 
			
		||||
    <i-bs  name="x"></i-bs> <ng-container i18n>Clear selection</ng-container>
 | 
			
		||||
    </button>
 | 
			
		||||
    <button type="button" class="btn btn-sm btn-outline-primary" (click)="setPermissions()" [disabled]="!userCanBulkEdit(PermissionAction.Change) || selectedObjects.size === 0">
 | 
			
		||||
    <button *ngIf="!permissionsDisabled" type="button" class="btn btn-sm btn-outline-primary" (click)="setPermissions()" [disabled]="!userCanBulkEdit(PermissionAction.Change) || 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)="delete()" [disabled]="!userCanBulkEdit(PermissionAction.Delete) || selectedObjects.size === 0">
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
 | 
			
		||||
    private modalService: NgbModal,
 | 
			
		||||
    private editDialogComponent: any,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private documentListViewService: DocumentListViewService,
 | 
			
		||||
    protected documentListViewService: DocumentListViewService,
 | 
			
		||||
    private permissionsService: PermissionsService,
 | 
			
		||||
    protected filterRuleType: number,
 | 
			
		||||
    public typeName: string,
 | 
			
		||||
@@ -93,6 +93,8 @@ export abstract class ManagementListComponent<T extends MatchingModel>
 | 
			
		||||
  public selectedObjects: Set<number> = new Set()
 | 
			
		||||
  public togggleAll: boolean = false
 | 
			
		||||
 | 
			
		||||
  protected permissionsDisabled: boolean = false
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.reloadData()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { ObjectWithId } from './object-with-id'
 | 
			
		||||
import { MatchingModel } from './matching-model'
 | 
			
		||||
 | 
			
		||||
export enum CustomFieldDataType {
 | 
			
		||||
  String = 'string',
 | 
			
		||||
@@ -51,13 +51,11 @@ export const DATA_TYPE_LABELS = [
 | 
			
		||||
  },
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export interface CustomField extends ObjectWithId {
 | 
			
		||||
export interface CustomField extends MatchingModel {
 | 
			
		||||
  data_type: CustomFieldDataType
 | 
			
		||||
  name: string
 | 
			
		||||
  created?: Date
 | 
			
		||||
  extra_data?: {
 | 
			
		||||
    select_options?: Array<{ label: string; id: string }>
 | 
			
		||||
    default_currency?: string
 | 
			
		||||
  }
 | 
			
		||||
  document_count?: number
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
import { HttpClient } from '@angular/common/http'
 | 
			
		||||
import { Injectable } from '@angular/core'
 | 
			
		||||
import { CustomField } from 'src/app/data/custom-field'
 | 
			
		||||
import { AbstractPaperlessService } from './abstract-paperless-service'
 | 
			
		||||
import { AbstractNameFilterService } from './abstract-name-filter-service'
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root',
 | 
			
		||||
})
 | 
			
		||||
export class CustomFieldsService extends AbstractPaperlessService<CustomField> {
 | 
			
		||||
export class CustomFieldsService extends AbstractNameFilterService<CustomField> {
 | 
			
		||||
  constructor(http: HttpClient) {
 | 
			
		||||
    super(http, 'custom_fields')
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user