diff --git a/front/app/src/app/views/base-view/list/list.component.html b/front/app/src/app/views/base-view/list/list.component.html index 514d337a..99f92f3e 100644 --- a/front/app/src/app/views/base-view/list/list.component.html +++ b/front/app/src/app/views/base-view/list/list.component.html @@ -2,6 +2,7 @@ [resource]="this.resource" [schema]="this.schema" [columns]="this.columns" + [filters]="this.filters" (result)="this.flashService.success($event)" (error)="this.flashService.error($event)"> \ No newline at end of file diff --git a/front/app/src/app/views/base-view/list/list.component.ts b/front/app/src/app/views/base-view/list/list.component.ts index 9d50abaf..56fea308 100644 --- a/front/app/src/app/views/base-view/list/list.component.ts +++ b/front/app/src/app/views/base-view/list/list.component.ts @@ -9,6 +9,7 @@ export class BaseCrudListComponent { @Input() resource: string = ""; @Input() columns: string[] = []; @Input() schema: string | undefined; + @Input() filters: string[] = []; constructor( public flashService: FlashmessagesService diff --git a/front/app/src/app/views/base-view/templates/list.template.html b/front/app/src/app/views/base-view/templates/list.template.html index 3b9cc863..f0b7ec17 100644 --- a/front/app/src/app/views/base-view/templates/list.template.html +++ b/front/app/src/app/views/base-view/templates/list.template.html @@ -1,5 +1,6 @@ + [columns]="this.columns" + [filters]="this.filters"> \ No newline at end of file diff --git a/front/app/src/common/crud/crud-formly-jsonschema.service.ts b/front/app/src/common/crud/crud-formly-jsonschema.service.ts index 275bb4da..cdbe25df 100644 --- a/front/app/src/common/crud/crud-formly-jsonschema.service.ts +++ b/front/app/src/common/crud/crud-formly-jsonschema.service.ts @@ -55,6 +55,8 @@ export class CrudFormlyJsonschemaOptions implements FormlyJsonschemaOptions { field.type = "richtext"; } else if (schema.type == "string" && schema.format == "signature-link") { field.type = "signature-link"; + } else if (field.type == "enum" && field.props.multiple) { + field.type = 'multicheckbox'; } if (schema.hasOwnProperty('props')) { diff --git a/front/app/src/common/crud/crud.module.ts b/front/app/src/common/crud/crud.module.ts index 7b9ab6c7..a5eb39e7 100644 --- a/front/app/src/common/crud/crud.module.ts +++ b/front/app/src/common/crud/crud.module.ts @@ -28,12 +28,14 @@ import { DictionaryService } from "./types/dictionary.service"; import { RichtextTypeComponent } from "./types/richtext.type"; import { SignatureLinkTypeComponent } from "@common/crud/types/signature-link.type"; import { ClipboardModule } from '@angular/cdk/clipboard'; +import {FilterListComponent} from "@common/crud/list/filter-list.component"; @NgModule({ declarations: [ CardComponent, ListComponent, + FilterListComponent, ObjectTypeComponent, DatetimeTypeComponent, DateTypeComponent, diff --git a/front/app/src/common/crud/jsonschemas.service.ts b/front/app/src/common/crud/jsonschemas.service.ts index 271020b2..de3a2efb 100644 --- a/front/app/src/common/crud/jsonschemas.service.ts +++ b/front/app/src/common/crud/jsonschemas.service.ts @@ -206,6 +206,21 @@ export class JsonschemasService { path.substring(pointFirstPosition + 1) ); } + + get_property_by_path(resource: JSONSchema7, path: string): JSONSchema7 { + const pointFirstPosition = path.indexOf('.') + if (pointFirstPosition == -1) { + return this.get_descendant(resource, path); + } + + return this.get_property_by_path( + this.get_descendant( + resource, + path.substring(0, pointFirstPosition) + ), + path.substring(pointFirstPosition + 1) + ); + } } export interface Schema { diff --git a/front/app/src/common/crud/list/filter-list.component.ts b/front/app/src/common/crud/list/filter-list.component.ts new file mode 100644 index 00000000..9e69fedd --- /dev/null +++ b/front/app/src/common/crud/list/filter-list.component.ts @@ -0,0 +1,88 @@ +import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import {JSONSchema7} from "json-schema"; +import {JsonschemasService} from "@common/crud/jsonschemas.service" +import {FormGroup} from "@angular/forms"; +import {FormlyFieldConfig} from "@ngx-formly/core"; +import {CrudFormlyJsonschemaService} from "@common/crud/crud-formly-jsonschema.service"; + +@Component({ + selector: 'crud-list-filter-list', + template: ` + + `, +}) +export class FilterListComponent implements OnInit { + @Input() filters: string[] = []; + @Input() schema = ""; + + @Output() filterChange: EventEmitter<{[key: string]: any}> = new EventEmitter(); + + form = new FormGroup({}); + fields: FormlyFieldConfig[] = []; + + searchTerms = {} + + public fieldJson = { + components: {}, + type: "object", + properties: {}, + } + + constructor(private jsonSchemasService: JsonschemasService, + private formlyJsonschema: CrudFormlyJsonschemaService,) { } + + ngOnInit() { + this.jsonSchemasService.getUpdateResource(this.schema!).subscribe({ + next: (schema: any) => this.getFilterDefinition(schema), + error: (err) => console.log(err) /*this.error.emit("Error loading the schema:" + err)*/ + }); + } + + getFilterDefinition(schema: JSONSchema7) { + const properties: {[key: string]: JSONSchema7} = {} + for (let filter of this.filters) { + if (this.jsonSchemasService.path_exists(schema, filter)) { + let prop = this.jsonSchemasService.get_property_by_path(schema, filter) + if (prop.hasOwnProperty('allOf')) { + // @ts-ignore + prop = schema.components.schemas[prop.allOf![0]['$ref'].replace('#/components/schemas/', '')]; + prop.type = "array"; + prop.items = {"type": "string", "enum": prop.enum}; + } + + if (prop.hasOwnProperty('readOnly') && prop.readOnly) { + prop.readOnly = false + } + properties[filter] = prop; + } + } + // @ts-ignore + this.fieldJson.components = schema.components; + this.fieldJson.properties = properties; + + // @ts-ignore + this.fields = [this.formlyJsonschema.toFieldConfig(this.fieldJson)] + } + + onModelChange(event: {[key: string]: any}) { + for (let p_name in event) { + // @ts-ignore + let p = this.fieldJson.properties[p_name] + if (p.type == "array" && !Array.isArray(p.items)) { + let value = [] + for (let key in event[p_name]) { + if (event[p_name][key]) { + value.push(key) + } + } + + if (value.length == 0 || value.length == p.items.enum.length) { + delete event[p_name]; + } else { + event[p_name] = value; + } + } + } + this.filterChange.next(event); + } +} diff --git a/front/app/src/common/crud/list/list.component.html b/front/app/src/common/crud/list/list.component.html index 25bf5009..a806a7a4 100644 --- a/front/app/src/common/crud/list/list.component.html +++ b/front/app/src/common/crud/list/list.component.html @@ -13,6 +13,9 @@ [(ngModel)]="searchTerm" /> +
+ +
Loading...
diff --git a/front/app/src/common/crud/list/list.component.ts b/front/app/src/common/crud/list/list.component.ts index 3989c594..7d281935 100644 --- a/front/app/src/common/crud/list/list.component.ts +++ b/front/app/src/common/crud/list/list.component.ts @@ -13,6 +13,7 @@ interface State { searchTerm: string; sortColumn: SortColumn; sortDirection: SortDirection; + searchFilters: {[key: string]: any} } @Component({ @@ -23,6 +24,7 @@ interface State { export class ListComponent implements OnInit { @Input() resource: string = ""; @Input() columns: string[] = []; + @Input() filters: string[] = []; @Input() schema: string | undefined; @Output() error: EventEmitter = new EventEmitter(); @@ -43,6 +45,7 @@ export class ListComponent implements OnInit { searchTerm: '', sortColumn: '_id', sortDirection: 'asc', + searchFilters: {} }; constructor(private service: CrudService, @@ -93,6 +96,13 @@ export class ListComponent implements OnInit { this._loading$.next(true); let sortBy = new SortBy(this.sortColumn, this.sortDirection) let filters = this.searchTerm ? [new Filters('fulltext', 'eq', this.searchTerm)] : []; + for (let f in this.searchFilters) { + if (Array.isArray(this.searchFilters[f])) { + filters.push(new Filters(f, 'in', this.searchFilters[f])) + } else { + filters.push(new Filters(f, 'eq', this.searchFilters[f])) + } + } this.service.getList(this.resource, this.page, this.pageSize, [sortBy], filters).subscribe({ next: (data: any) => { @@ -107,6 +117,10 @@ export class ListComponent implements OnInit { }); } + onFilterChange(event: any) { + this.searchFilters = event; + } + onSort({ column, direction }: any) { // resetting other headers this.headers.forEach((header) => { @@ -151,6 +165,9 @@ export class ListComponent implements OnInit { get searchTerm() { return this._state.searchTerm; } + get searchFilters() { + return this._state.searchFilters; + } set page(page: number) { this._set({ page }); @@ -161,6 +178,9 @@ export class ListComponent implements OnInit { set searchTerm(searchTerm: string) { this._set({ searchTerm }); } + set searchFilters(searchFilters: {[key: string]: any}) { + this._set({ searchFilters }); + } set sortColumn(sortColumn: SortColumn) { this._set({ sortColumn }); }