Gotta start somewhere
This commit is contained in:
parent
d7ba6d98d3
commit
f0157a36fb
@ -108,6 +108,7 @@ import { FileDropComponent } from './components/file-drop/file-drop.component'
|
||||
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
|
||||
import { CustomFieldEditDialogComponent } from './components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
|
||||
import { CustomFieldsDropdownComponent } from './components/common/custom-fields-dropdown/custom-fields-dropdown.component'
|
||||
import { CustomFieldsLookupDropdownComponent } from './components/common/custom-fields-lookup-dropdown/custom-fields-lookup-dropdown.component'
|
||||
import { ProfileEditDialogComponent } from './components/common/profile-edit-dialog/profile-edit-dialog.component'
|
||||
import { PdfViewerModule } from 'ng2-pdf-viewer'
|
||||
import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component'
|
||||
@ -485,6 +486,7 @@ function initializeApp(settings: SettingsService) {
|
||||
CustomFieldsComponent,
|
||||
CustomFieldEditDialogComponent,
|
||||
CustomFieldsDropdownComponent,
|
||||
CustomFieldsLookupDropdownComponent,
|
||||
ProfileEditDialogComponent,
|
||||
DocumentLinkComponent,
|
||||
PreviewPopupComponent,
|
||||
|
@ -0,0 +1,44 @@
|
||||
<div class="btn-group w-100" ngbDropdown role="group" #dropdown="ngbDropdown">
|
||||
<button class="btn btn-sm btn-outline-primary" id="dropdown_{{name}}" ngbDropdownToggle [disabled]="disabled">
|
||||
<i-bs name="{{icon}}"></i-bs>
|
||||
<div class="d-none d-sm-inline"> {{title}}</div>
|
||||
</button>
|
||||
<div class="dropdown-menu shadow" ngbDropdownMenu attr.aria-labelledby="dropdown_{{name}}">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary ms-3" (click)="addQuery()" [disabled]="disabled">
|
||||
<i-bs name="plus"></i-bs> Add query
|
||||
</button>
|
||||
<div class="list-group list-group-flush">
|
||||
@for (query of selectionModel.queries; track query; let i = $index) {
|
||||
<div class="list-group-item d-flex">
|
||||
<div class="input-group input-group-sm flex-shrink-1 flex-nowrap">
|
||||
<ng-select
|
||||
class="w-50"
|
||||
[items]="customFields"
|
||||
[(ngModel)]="query.field"
|
||||
[disabled]="disabled"
|
||||
bindLabel="name"
|
||||
bindValue="id"
|
||||
></ng-select>
|
||||
<select class="w-25 form-control" [(ngModel)]="query.operator" [disabled]="disabled">
|
||||
<option *ngFor="let operator of getOperatorsForField(query.field)" [ngValue]="operator">{{operator}}</option>
|
||||
</select>
|
||||
@switch (query.operator) {
|
||||
@case ('exists') {
|
||||
<select class="w-25 form-control" [(ngModel)]="query.value" [disabled]="disabled">
|
||||
<option value="true" i18n>true</option>
|
||||
<option value="false" i18n>false</option>
|
||||
</select>
|
||||
}
|
||||
@default {
|
||||
<input class="w-25 form-control" type="text" [(ngModel)]="query.value" [disabled]="disabled">
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<button class="btn btn-link btn-sm pe-0" type="button" (click)="removeQuery(i)" [disabled]="disabled">
|
||||
<i-bs name="x"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,8 @@
|
||||
.dropdown-menu {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
::ng-deep .ng-select-container {
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||
import { Subject, first, takeUntil } from 'rxjs'
|
||||
import { CustomField } from 'src/app/data/custom-field'
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
|
||||
export class CustomFieldQuery {
|
||||
public changed = new Subject<CustomFieldQuery>()
|
||||
|
||||
private _field: string
|
||||
set field(value: string) {
|
||||
this._field = value
|
||||
this.changed.next(this)
|
||||
}
|
||||
get field(): string {
|
||||
return this._field
|
||||
}
|
||||
|
||||
private _operator: string
|
||||
set operator(value: string) {
|
||||
this._operator = value
|
||||
this.changed.next(this)
|
||||
}
|
||||
get operator(): string {
|
||||
return this._operator
|
||||
}
|
||||
|
||||
private _value: string
|
||||
set value(value: string) {
|
||||
this._value = value
|
||||
this.changed.next(this)
|
||||
}
|
||||
get value(): string {
|
||||
return this._value
|
||||
}
|
||||
|
||||
constructor(
|
||||
field: string = null,
|
||||
operator: string = null,
|
||||
value: string = null
|
||||
) {
|
||||
this.field = field
|
||||
this.operator = operator
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomFieldQueriesModel {
|
||||
// matchingModel: MatchingModel
|
||||
queries: CustomFieldQuery[] = []
|
||||
|
||||
changed = new Subject<CustomFieldQueriesModel>()
|
||||
|
||||
public clear(fireEvent = true) {
|
||||
this.queries = []
|
||||
if (fireEvent) {
|
||||
this.changed.next(this)
|
||||
}
|
||||
}
|
||||
|
||||
public addQuery(query: CustomFieldQuery = new CustomFieldQuery()) {
|
||||
this.queries.push(query)
|
||||
query.changed.subscribe(() => {
|
||||
if (query.field && query.operator && query.value) {
|
||||
this.changed.next(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public removeQuery(index: number) {
|
||||
const query = this.queries.splice(index, 1)[0]
|
||||
query.changed.complete()
|
||||
this.changed.next(this)
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-custom-fields-lookup-dropdown',
|
||||
templateUrl: './custom-fields-lookup-dropdown.component.html',
|
||||
styleUrls: ['./custom-fields-lookup-dropdown.component.scss'],
|
||||
})
|
||||
export class CustomFieldsLookupDropdownComponent {
|
||||
@Input()
|
||||
title: string
|
||||
|
||||
@Input()
|
||||
filterPlaceholder: string = ''
|
||||
|
||||
@Input()
|
||||
icon: string
|
||||
|
||||
@Input()
|
||||
allowSelectNone: boolean = false
|
||||
|
||||
@Input()
|
||||
editing = false
|
||||
|
||||
@Input()
|
||||
applyOnClose = false
|
||||
|
||||
get name(): string {
|
||||
return this.title ? this.title.replace(/\s/g, '_').toLowerCase() : null
|
||||
}
|
||||
|
||||
@Input()
|
||||
disabled: boolean = false
|
||||
|
||||
_selectionModel: CustomFieldQueriesModel = new CustomFieldQueriesModel()
|
||||
|
||||
@Input()
|
||||
set selectionModel(model: CustomFieldQueriesModel) {
|
||||
model.changed.subscribe((updatedModel) => {
|
||||
this.selectionModelChange.next(updatedModel)
|
||||
})
|
||||
this._selectionModel = model
|
||||
}
|
||||
|
||||
get selectionModel(): CustomFieldQueriesModel {
|
||||
return this._selectionModel
|
||||
}
|
||||
|
||||
@Output()
|
||||
selectionModelChange = new EventEmitter<CustomFieldQueriesModel>()
|
||||
|
||||
customFields: CustomField[] = []
|
||||
|
||||
private unsubscribeNotifier: Subject<any> = new Subject()
|
||||
|
||||
constructor(protected customFieldsService: CustomFieldsService) {
|
||||
this.getFields()
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.unsubscribeNotifier.next(this)
|
||||
this.unsubscribeNotifier.complete()
|
||||
}
|
||||
|
||||
private getFields() {
|
||||
this.customFieldsService
|
||||
.listAll()
|
||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe((result) => {
|
||||
this.customFields = result.results
|
||||
})
|
||||
}
|
||||
|
||||
public addQuery() {
|
||||
this.selectionModel.addQuery()
|
||||
}
|
||||
|
||||
public removeQuery(index: number) {
|
||||
this.selectionModel.removeQuery(index)
|
||||
}
|
||||
|
||||
getOperatorsForField(field: CustomField): string[] {
|
||||
return ['exact', 'in', 'isnull', 'exists']
|
||||
// TODO: implement this
|
||||
}
|
||||
}
|
@ -86,15 +86,10 @@
|
||||
}
|
||||
|
||||
@if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.CustomField) && customFields.length > 0) {
|
||||
<pngx-filterable-dropdown class="flex-fill" title="Custom fields" icon="ui-radios" i18n-title
|
||||
filterPlaceholder="Filter custom fields" i18n-filterPlaceholder
|
||||
[items]="customFields"
|
||||
[manyToOne]="true"
|
||||
[(selectionModel)]="customFieldSelectionModel"
|
||||
<pngx-custom-fields-lookup-dropdown class="flex-fill" title="Custom fields" icon="ui-radios" i18n-title
|
||||
[(selectionModel)]="customFieldQueriesModel"
|
||||
(selectionModelChange)="updateRules()"
|
||||
(opened)="onCustomFieldsDropdownOpen()"
|
||||
[documentCounts]="customFieldDocumentCounts"
|
||||
[allowSelectNone]="true"></pngx-filterable-dropdown>
|
||||
></pngx-custom-fields-lookup-dropdown>
|
||||
}
|
||||
<pngx-dates-dropdown
|
||||
title="Dates" i18n-title
|
||||
|
@ -839,9 +839,7 @@ describe('FilterEditorComponent', () => {
|
||||
}))
|
||||
|
||||
it('should ingest filter rules for has all custom fields', fakeAsync(() => {
|
||||
expect(component.customFieldSelectionModel.getSelectedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
expect(component.customFieldQueriesModel.getSelectedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_HAS_CUSTOM_FIELDS_ALL,
|
||||
@ -852,10 +850,10 @@ describe('FilterEditorComponent', () => {
|
||||
value: '43',
|
||||
},
|
||||
]
|
||||
expect(component.customFieldSelectionModel.logicalOperator).toEqual(
|
||||
expect(component.customFieldQueriesModel.logicalOperator).toEqual(
|
||||
LogicalOperator.And
|
||||
)
|
||||
expect(component.customFieldSelectionModel.getSelectedItems()).toEqual(
|
||||
expect(component.customFieldQueriesModel.getSelectedItems()).toEqual(
|
||||
custom_fields
|
||||
)
|
||||
// coverage
|
||||
@ -869,9 +867,7 @@ describe('FilterEditorComponent', () => {
|
||||
}))
|
||||
|
||||
it('should ingest filter rules for has any custom fields', fakeAsync(() => {
|
||||
expect(component.customFieldSelectionModel.getSelectedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
expect(component.customFieldQueriesModel.getSelectedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_HAS_CUSTOM_FIELDS_ANY,
|
||||
@ -882,10 +878,10 @@ describe('FilterEditorComponent', () => {
|
||||
value: '43',
|
||||
},
|
||||
]
|
||||
expect(component.customFieldSelectionModel.logicalOperator).toEqual(
|
||||
expect(component.customFieldQueriesModel.logicalOperator).toEqual(
|
||||
LogicalOperator.Or
|
||||
)
|
||||
expect(component.customFieldSelectionModel.getSelectedItems()).toEqual(
|
||||
expect(component.customFieldQueriesModel.getSelectedItems()).toEqual(
|
||||
custom_fields
|
||||
)
|
||||
// coverage
|
||||
@ -898,25 +894,19 @@ describe('FilterEditorComponent', () => {
|
||||
}))
|
||||
|
||||
it('should ingest filter rules for has any custom field', fakeAsync(() => {
|
||||
expect(component.customFieldSelectionModel.getSelectedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
expect(component.customFieldQueriesModel.getSelectedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_HAS_ANY_CUSTOM_FIELDS,
|
||||
value: '1',
|
||||
},
|
||||
]
|
||||
expect(component.customFieldSelectionModel.getSelectedItems()).toHaveLength(
|
||||
1
|
||||
)
|
||||
expect(component.customFieldSelectionModel.get(null)).toBeTruthy()
|
||||
expect(component.customFieldQueriesModel.getSelectedItems()).toHaveLength(1)
|
||||
expect(component.customFieldQueriesModel.get(null)).toBeTruthy()
|
||||
}))
|
||||
|
||||
it('should ingest filter rules for exclude tag(s)', fakeAsync(() => {
|
||||
expect(component.customFieldSelectionModel.getExcludedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
expect(component.customFieldQueriesModel.getExcludedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS,
|
||||
@ -927,10 +917,10 @@ describe('FilterEditorComponent', () => {
|
||||
value: '43',
|
||||
},
|
||||
]
|
||||
expect(component.customFieldSelectionModel.logicalOperator).toEqual(
|
||||
expect(component.customFieldQueriesModel.logicalOperator).toEqual(
|
||||
LogicalOperator.And
|
||||
)
|
||||
expect(component.customFieldSelectionModel.getExcludedItems()).toEqual(
|
||||
expect(component.customFieldQueriesModel.getExcludedItems()).toEqual(
|
||||
custom_fields
|
||||
)
|
||||
// coverage
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
import { Tag } from 'src/app/data/tag'
|
||||
import { Correspondent } from 'src/app/data/correspondent'
|
||||
import { DocumentType } from 'src/app/data/document-type'
|
||||
import { Observable, Subject, Subscription, from } from 'rxjs'
|
||||
import { Observable, Subject, from } from 'rxjs'
|
||||
import {
|
||||
catchError,
|
||||
debounceTime,
|
||||
@ -63,6 +63,7 @@ import {
|
||||
FILTER_HAS_CUSTOM_FIELDS_ALL,
|
||||
FILTER_HAS_ANY_CUSTOM_FIELDS,
|
||||
FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS,
|
||||
FILTER_CUSTOM_FIELDS_LOOKUP,
|
||||
} from 'src/app/data/filter-rule-type'
|
||||
import {
|
||||
FilterableDropdownSelectionModel,
|
||||
@ -92,13 +93,16 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
import { CustomField } from 'src/app/data/custom-field'
|
||||
import { SearchService } from 'src/app/services/rest/search.service'
|
||||
import {
|
||||
CustomFieldQueriesModel,
|
||||
CustomFieldQuery,
|
||||
} from '../../common/custom-fields-lookup-dropdown/custom-fields-lookup-dropdown.component'
|
||||
|
||||
const TEXT_FILTER_TARGET_TITLE = 'title'
|
||||
const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content'
|
||||
const TEXT_FILTER_TARGET_ASN = 'asn'
|
||||
const TEXT_FILTER_TARGET_FULLTEXT_QUERY = 'fulltext-query'
|
||||
const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = 'fulltext-morelike'
|
||||
const TEXT_FILTER_TARGET_CUSTOM_FIELDS = 'custom-fields'
|
||||
|
||||
const TEXT_FILTER_MODIFIER_EQUALS = 'equals'
|
||||
const TEXT_FILTER_MODIFIER_NULL = 'is null'
|
||||
@ -134,10 +138,6 @@ const DEFAULT_TEXT_FILTER_TARGET_OPTIONS = [
|
||||
name: $localize`Title & content`,
|
||||
},
|
||||
{ id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN` },
|
||||
{
|
||||
id: TEXT_FILTER_TARGET_CUSTOM_FIELDS,
|
||||
name: $localize`Custom fields`,
|
||||
},
|
||||
{
|
||||
id: TEXT_FILTER_TARGET_FULLTEXT_QUERY,
|
||||
name: $localize`Advanced search`,
|
||||
@ -321,7 +321,7 @@ export class FilterEditorComponent
|
||||
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
||||
documentTypeSelectionModel = new FilterableDropdownSelectionModel()
|
||||
storagePathSelectionModel = new FilterableDropdownSelectionModel()
|
||||
customFieldSelectionModel = new FilterableDropdownSelectionModel()
|
||||
customFieldQueriesModel = new CustomFieldQueriesModel()
|
||||
|
||||
dateCreatedBefore: string
|
||||
dateCreatedAfter: string
|
||||
@ -356,7 +356,7 @@ export class FilterEditorComponent
|
||||
this.storagePathSelectionModel.clear(false)
|
||||
this.tagSelectionModel.clear(false)
|
||||
this.correspondentSelectionModel.clear(false)
|
||||
this.customFieldSelectionModel.clear(false)
|
||||
this.customFieldQueriesModel.clear(false)
|
||||
this._textFilter = null
|
||||
this._moreLikeId = null
|
||||
this.dateAddedBefore = null
|
||||
@ -383,8 +383,7 @@ export class FilterEditorComponent
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_ASN
|
||||
break
|
||||
case FILTER_CUSTOM_FIELDS_TEXT:
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_CUSTOM_FIELDS
|
||||
console.log('FILTER_CUSTOM_FIELDS_TEXT', rule.value)
|
||||
break
|
||||
case FILTER_FULLTEXT_QUERY:
|
||||
let allQueryArgs = rule.value.split(',')
|
||||
@ -523,35 +522,28 @@ export class FilterEditorComponent
|
||||
false
|
||||
)
|
||||
break
|
||||
case FILTER_HAS_CUSTOM_FIELDS_ALL:
|
||||
this.customFieldSelectionModel.logicalOperator = LogicalOperator.And
|
||||
this.customFieldSelectionModel.set(
|
||||
rule.value ? +rule.value : null,
|
||||
ToggleableItemState.Selected,
|
||||
false
|
||||
case FILTER_CUSTOM_FIELDS_LOOKUP:
|
||||
// TODO: fully implement
|
||||
const query = JSON.parse(rule.value)
|
||||
this.customFieldQueriesModel.addQuery(
|
||||
new CustomFieldQuery(query[0], query[1], query[2])
|
||||
)
|
||||
break
|
||||
case FILTER_HAS_CUSTOM_FIELDS_ALL:
|
||||
console.log('FILTER_HAS_CUSTOM_FIELDS_ALL', rule.value)
|
||||
// TODO: fully implement
|
||||
break
|
||||
case FILTER_HAS_CUSTOM_FIELDS_ANY:
|
||||
this.customFieldSelectionModel.logicalOperator = LogicalOperator.Or
|
||||
this.customFieldSelectionModel.set(
|
||||
rule.value ? +rule.value : null,
|
||||
ToggleableItemState.Selected,
|
||||
false
|
||||
)
|
||||
console.log('FILTER_HAS_CUSTOM_FIELDS_ANY', rule.value)
|
||||
// TODO: fully implement
|
||||
break
|
||||
case FILTER_HAS_ANY_CUSTOM_FIELDS:
|
||||
this.customFieldSelectionModel.set(
|
||||
null,
|
||||
ToggleableItemState.Selected,
|
||||
false
|
||||
)
|
||||
console.log('FILTER_HAS_ANY_CUSTOM_FIELDS', rule.value)
|
||||
// TODO: fully implement
|
||||
break
|
||||
case FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS:
|
||||
this.customFieldSelectionModel.set(
|
||||
rule.value ? +rule.value : null,
|
||||
ToggleableItemState.Excluded,
|
||||
false
|
||||
)
|
||||
console.log('FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS', rule.value)
|
||||
// TODO: fully implement
|
||||
break
|
||||
case FILTER_ASN_ISNULL:
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_ASN
|
||||
@ -655,15 +647,6 @@ export class FilterEditorComponent
|
||||
})
|
||||
}
|
||||
}
|
||||
if (
|
||||
this._textFilter &&
|
||||
this.textFilterTarget == TEXT_FILTER_TARGET_CUSTOM_FIELDS
|
||||
) {
|
||||
filterRules.push({
|
||||
rule_type: FILTER_CUSTOM_FIELDS_TEXT,
|
||||
value: this._textFilter,
|
||||
})
|
||||
}
|
||||
if (
|
||||
this._textFilter &&
|
||||
this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_QUERY
|
||||
@ -768,35 +751,24 @@ export class FilterEditorComponent
|
||||
})
|
||||
})
|
||||
}
|
||||
if (this.customFieldSelectionModel.isNoneSelected()) {
|
||||
let queries = this.customFieldQueriesModel.queries
|
||||
.filter((query) => query.field && query.operator)
|
||||
.map((query) => [query.field, query.operator, query.value])
|
||||
console.log(
|
||||
'this.customFieldQueriesModel.queries',
|
||||
this.customFieldQueriesModel.queries
|
||||
)
|
||||
console.log('queries', queries)
|
||||
if (queries.length > 0) {
|
||||
filterRules.push({
|
||||
rule_type: FILTER_HAS_ANY_CUSTOM_FIELDS,
|
||||
value: 'false',
|
||||
rule_type: FILTER_CUSTOM_FIELDS_LOOKUP,
|
||||
value:
|
||||
queries.length === 1
|
||||
? JSON.stringify(queries[0])
|
||||
: JSON.stringify(queries),
|
||||
})
|
||||
} else {
|
||||
const customFieldFilterType =
|
||||
this.customFieldSelectionModel.logicalOperator == LogicalOperator.And
|
||||
? FILTER_HAS_CUSTOM_FIELDS_ALL
|
||||
: FILTER_HAS_CUSTOM_FIELDS_ANY
|
||||
this.customFieldSelectionModel
|
||||
.getSelectedItems()
|
||||
.filter((field) => field.id)
|
||||
.forEach((field) => {
|
||||
filterRules.push({
|
||||
rule_type: customFieldFilterType,
|
||||
value: field.id?.toString(),
|
||||
})
|
||||
})
|
||||
this.customFieldSelectionModel
|
||||
.getExcludedItems()
|
||||
.filter((field) => field.id)
|
||||
.forEach((field) => {
|
||||
filterRules.push({
|
||||
rule_type: FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS,
|
||||
value: field.id?.toString(),
|
||||
})
|
||||
})
|
||||
}
|
||||
// TODO: fully implement custom fields
|
||||
if (this.dateCreatedBefore) {
|
||||
filterRules.push({
|
||||
rule_type: FILTER_CREATED_BEFORE,
|
||||
@ -1079,10 +1051,6 @@ export class FilterEditorComponent
|
||||
this.storagePathSelectionModel.apply()
|
||||
}
|
||||
|
||||
onCustomFieldsDropdownOpen() {
|
||||
this.customFieldSelectionModel.apply()
|
||||
}
|
||||
|
||||
updateTextFilter(text, updateRules = true) {
|
||||
this._textFilter = text
|
||||
if (updateRules) {
|
||||
|
@ -55,6 +55,8 @@ export const FILTER_HAS_CUSTOM_FIELDS_ANY = 39
|
||||
export const FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS = 40
|
||||
export const FILTER_HAS_ANY_CUSTOM_FIELDS = 41
|
||||
|
||||
export const FILTER_CUSTOM_FIELDS_LOOKUP = 42
|
||||
|
||||
export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
||||
{
|
||||
id: FILTER_TITLE,
|
||||
@ -317,6 +319,12 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
||||
multi: false,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: FILTER_CUSTOM_FIELDS_LOOKUP,
|
||||
filtervar: 'custom_field_lookup',
|
||||
datatype: 'string',
|
||||
multi: false,
|
||||
},
|
||||
]
|
||||
|
||||
export interface FilterRuleType {
|
||||
|
@ -507,6 +507,7 @@ class SavedViewFilterRule(models.Model):
|
||||
(39, _("has custom field in")),
|
||||
(40, _("does not have custom field in")),
|
||||
(41, _("does not have custom field")),
|
||||
(42, _("custom fields lookup")),
|
||||
]
|
||||
|
||||
saved_view = models.ForeignKey(
|
||||
|
Loading…
x
Reference in New Issue
Block a user