mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
committed by
Lorenz Hilpert
parent
5143e13ebe
commit
acadada245
@@ -16,7 +16,7 @@ import {
|
||||
OrderByDTO,
|
||||
QueryTokenDTO,
|
||||
} from '@swagger/cat';
|
||||
import { Filter, FilterType, SelectFilter, SelectFilterGroup, SelectFilterOption } from '@ui/filter';
|
||||
import { Filter, FilterType, RangeFilter, RangeFilterOption, SelectFilter, SelectFilterGroup, SelectFilterOption } from '@ui/filter';
|
||||
import { Observable } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, exhaustMap, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { ScrollPositionService } from 'apps/ui/common/src/lib/scroll-position/scroll-position.service';
|
||||
@@ -131,11 +131,6 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
tapResponse(
|
||||
(response: InputGroupDTO[]) => {
|
||||
const mappedInputGroup = this.mapInputArraysToFilterGroupArrayInsideInputGroup(response);
|
||||
|
||||
// TODO: Reading Age
|
||||
mappedInputGroup[2].input.splice(3, 1);
|
||||
console.log(mappedInputGroup);
|
||||
|
||||
const initialFilter = this.concatNestedArrays(mappedInputGroup.filter((filters) => filters.group === 'filter'));
|
||||
const initialprimaryFilter = this.concatNestedArrays(
|
||||
mappedInputGroup.filter((filters) => filters.group === 'main' || filters.group === 'input_selector')
|
||||
@@ -374,15 +369,28 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
return arr;
|
||||
}
|
||||
|
||||
private fromInputDto(input: InputDTO): SelectFilter {
|
||||
return {
|
||||
key: input.key,
|
||||
name: input.label,
|
||||
max: input.options && input.options.max,
|
||||
type: this.inputTypeToType(input.type),
|
||||
target: input.target,
|
||||
options: input.options && input.options.values.map((v) => this.fromOptionDto(v)),
|
||||
} as SelectFilter;
|
||||
private fromInputDto(input: InputDTO): SelectFilter | RangeFilter {
|
||||
if (this.inputTypeToType(input.type) === 'integer-range') {
|
||||
return {
|
||||
key: input.key,
|
||||
name: input.label,
|
||||
description: input.description,
|
||||
type: this.inputTypeToType(input.type),
|
||||
target: input.target,
|
||||
options: input.options && input.options.values.map((v) => this.fromOptionDtoRange(v)),
|
||||
} as RangeFilter;
|
||||
}
|
||||
|
||||
if (this.inputTypeToType(input.type) !== 'integer-range') {
|
||||
return {
|
||||
key: input.key,
|
||||
name: input.label,
|
||||
max: input.options && input.options.max,
|
||||
type: this.inputTypeToType(input.type),
|
||||
target: input.target,
|
||||
options: input.options && input.options.values.map((v) => this.fromOptionDto(v)),
|
||||
} as SelectFilter;
|
||||
}
|
||||
}
|
||||
|
||||
private fromOptionDto(option: OptionDTO): SelectFilterOption {
|
||||
@@ -396,6 +404,18 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
} as SelectFilterOption;
|
||||
}
|
||||
|
||||
private fromOptionDtoRange(option: OptionDTO): RangeFilterOption {
|
||||
return {
|
||||
name: option.label,
|
||||
selected: option.selected || false,
|
||||
value: '',
|
||||
id: option.key || option.value,
|
||||
minValue: option.minValue,
|
||||
maxValue: option.maxValue,
|
||||
placeholder: option.placeholder,
|
||||
} as RangeFilterOption;
|
||||
}
|
||||
|
||||
private inputTypeToType(type: InputType): FilterType {
|
||||
switch (type) {
|
||||
case 1:
|
||||
@@ -413,6 +433,9 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
case 32 || 64:
|
||||
return 'number';
|
||||
|
||||
case 4096:
|
||||
return 'integer-range';
|
||||
|
||||
default:
|
||||
return 'select';
|
||||
}
|
||||
@@ -423,7 +446,22 @@ export class ArticleSearchStore extends ComponentStore<ArticleSearchState> {
|
||||
for (const filter of source) {
|
||||
const selected = filter.options.filter((o) => o.selected);
|
||||
if (selected.length > 0) {
|
||||
dict[filter.key] = selected.map((o) => o.id).join(';');
|
||||
dict[filter.key] = selected
|
||||
.map((o: SelectFilterOption | RangeFilterOption) => {
|
||||
if (filter.key === 'reading-age') {
|
||||
if (o.id === 'start') {
|
||||
return `${(o as RangeFilterOption).value}-`;
|
||||
} else if (o.id === 'stop') {
|
||||
return `-${(o as RangeFilterOption).value}`;
|
||||
}
|
||||
} else {
|
||||
return o.id;
|
||||
}
|
||||
})
|
||||
.join(';');
|
||||
}
|
||||
if (selected.find((o) => o.id === 'start') && selected.find((o) => o.id === 'stop')) {
|
||||
dict['reading-age'] = dict['reading-age'].replace('-;', '');
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
|
||||
@@ -3,8 +3,11 @@ import { InputOptionsDTO } from './input-options-dto';
|
||||
import { InputType } from './input-type';
|
||||
export interface InputDTO {
|
||||
constraint?: string;
|
||||
description?: string;
|
||||
key?: string;
|
||||
label?: string;
|
||||
maxValue?: string;
|
||||
minValue?: string;
|
||||
options?: InputOptionsDTO;
|
||||
placeholder?: string;
|
||||
target?: string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* tslint:disable */
|
||||
import { InputDTO } from './input-dto';
|
||||
export interface InputGroupDTO {
|
||||
description?: string;
|
||||
group?: string;
|
||||
input?: Array<InputDTO>;
|
||||
label?: string;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
/* tslint:disable */
|
||||
export interface OptionDTO {
|
||||
description?: string;
|
||||
enabled?: boolean;
|
||||
key?: string;
|
||||
label?: string;
|
||||
maxValue?: string;
|
||||
minValue?: string;
|
||||
placeholder?: string;
|
||||
selected?: boolean;
|
||||
value?: string;
|
||||
values?: Array<OptionDTO>;
|
||||
|
||||
@@ -24,5 +24,6 @@
|
||||
[selectionContainer]="selectionContainerRef"
|
||||
>
|
||||
</ui-select-filter>
|
||||
<ui-range-filter *ngSwitchCase="'integer-range'" (category)="setActive($event)" [filter]="active" [module]="module"> </ui-range-filter>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './range-filter.component';
|
||||
@@ -0,0 +1,23 @@
|
||||
<div #selectFilter class="isa-card">
|
||||
<div class="select-filter-options isa-mt-10">
|
||||
<p class="range-filter-headline">{{ filter.description }}</p>
|
||||
<div [formGroup]="formGroup" class="range-input-wrapper">
|
||||
<input
|
||||
class="range-filter-input"
|
||||
uiInput
|
||||
type="string"
|
||||
[tabindex]="0"
|
||||
[formControlName]="'start'"
|
||||
[placeholder]="filter.options[0].name"
|
||||
/>
|
||||
<input
|
||||
class="range-filter-input"
|
||||
uiInput
|
||||
type="string"
|
||||
[tabindex]="1"
|
||||
[formControlName]="'stop'"
|
||||
[placeholder]="filter.options[1].name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
.select-filter-options {
|
||||
@apply pr-px-50;
|
||||
}
|
||||
|
||||
.range-filter-headline {
|
||||
@apply font-semibold text-base;
|
||||
}
|
||||
|
||||
.range-input-wrapper {
|
||||
@apply flex flex-row justify-between mb-6;
|
||||
}
|
||||
|
||||
.range-filter-input {
|
||||
@apply pt-px-20 font-bold text-xl mr-8;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-bottom: 2px solid #e1ebf5;
|
||||
}
|
||||
|
||||
.range-filter-input::placeholder {
|
||||
@apply font-bold text-xl text-cool-grey;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, ElementRef, ChangeDetectorRef, OnInit, Output, EventEmitter } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors } from '@angular/forms';
|
||||
import { RangeFilter, RangeFilterOption, SelectFilter } from '../../../models';
|
||||
import { FilterGroup } from '../../filter-group';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-range-filter',
|
||||
templateUrl: 'range-filter.component.html',
|
||||
styleUrls: ['range-filter.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UiRangeFilterComponent implements OnInit {
|
||||
@Output() category = new EventEmitter<SelectFilter | RangeFilter>();
|
||||
|
||||
@Input()
|
||||
filter: RangeFilter;
|
||||
|
||||
@Input() selectionContainer: ElementRef;
|
||||
|
||||
@Input() module: 'Customer' | 'Branch' = 'Branch';
|
||||
|
||||
formGroup: FormGroup;
|
||||
|
||||
startValidator = (opt: RangeFilterOption) => (control: AbstractControl): null | ValidationErrors => {
|
||||
const stopCtrl = control?.parent?.get('stop');
|
||||
if (control.value !== opt.value) {
|
||||
this.updateValue(control.value, opt, control, { compareControlValue: stopCtrl.value, target: 'stop' });
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
stopValidator = (opt: RangeFilterOption) => (control: AbstractControl): null | ValidationErrors => {
|
||||
const startCtrl = control?.parent?.get('start');
|
||||
if (control.value !== opt.value) {
|
||||
this.updateValue(control.value, opt, control, { compareControlValue: startCtrl.value, target: 'start' });
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
constructor(protected cdr: ChangeDetectorRef, public filterGroup: FilterGroup, private fb: FormBuilder) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.formGroup = this.fb.group({
|
||||
start: this.fb.control(this.filter.options[0].value, [this.startValidator(this.filter.options[0])]),
|
||||
stop: this.fb.control(this.filter.options[1].value, [this.stopValidator(this.filter.options[1])]),
|
||||
});
|
||||
|
||||
this.filterGroup.updateView.subscribe((_) => this.cdr.markForCheck());
|
||||
}
|
||||
|
||||
updateValue(
|
||||
value: string,
|
||||
option: RangeFilterOption,
|
||||
control: AbstractControl,
|
||||
compareValue: { compareControlValue: string; target: 'start' | 'stop' }
|
||||
) {
|
||||
value === '' ? (option.selected = true) : (option.selected = false);
|
||||
option.value = this.checkValue(value, option, compareValue);
|
||||
this.updateControl(option.value, control);
|
||||
this.filterGroup.toggleOption(this.filter, option);
|
||||
this.preSelectCategory(this.filter);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
updateControl(value: string, control: AbstractControl) {
|
||||
control.setValue(value, { emitEvent: false });
|
||||
}
|
||||
|
||||
checkValue(value: string, option: RangeFilterOption, compareValue: { compareControlValue: string; target: 'start' | 'stop' }): string {
|
||||
let validatedResult =
|
||||
Number(value) <= option.maxValue && Number(value) >= option.minValue
|
||||
? value
|
||||
.replace(/[^0-9.]/g, '') // Only Numbers
|
||||
.replace(/^(?:0+(?=[1-9])|0+(?=0$))/gm, '') // No leading Zeros and only one Zero allowed
|
||||
.replace(/(\..*)\./g, '$1') // No special characters
|
||||
: Number(value) > option.maxValue
|
||||
? `${option.maxValue}`
|
||||
: Number(value) < option.minValue
|
||||
? `${option.minValue}`
|
||||
: '';
|
||||
|
||||
// Checking if start value is smaller than stop value
|
||||
if (compareValue.target === 'stop' && compareValue.compareControlValue !== '') {
|
||||
if (Number(compareValue.compareControlValue) < Number(validatedResult)) {
|
||||
validatedResult = compareValue.compareControlValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Checking if start value is smaller than stop value and vice versa
|
||||
// if (compareValue.target === 'start' && compareValue.compareControlValue !== '') {
|
||||
// if (Number(compareValue.compareControlValue) > Number(validatedResult)) {
|
||||
// // validatedResult = compareValue.compareControlValue;
|
||||
// }
|
||||
// }
|
||||
|
||||
return validatedResult;
|
||||
}
|
||||
|
||||
preSelectCategory(category: SelectFilter | RangeFilter) {
|
||||
this.category.emit(category);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,15 @@ import { NgModule } from '@angular/core';
|
||||
import { UiFilterGroupComponent } from './components/filter-group';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { UiSelectFilterComponent, UiSelectFilterOptionComponent } from './components/select-filter';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiRangeFilterComponent } from './components/range-filter';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiIconModule, FormsModule],
|
||||
imports: [CommonModule, UiIconModule, FormsModule, UiInputModule, ReactiveFormsModule],
|
||||
exports: [UiFilterGroupComponent],
|
||||
declarations: [UiFilterGroupComponent, UiSelectFilterComponent, UiSelectFilterOptionComponent],
|
||||
declarations: [UiFilterGroupComponent, UiSelectFilterComponent, UiSelectFilterOptionComponent, UiRangeFilterComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class UiFilterGroupModule {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { Filter, FilterOption } from '../models';
|
||||
import { Filter, FilterOption, RangeFilter } from '../models';
|
||||
import { isBoolean } from '@utils/common';
|
||||
|
||||
export abstract class FilterGroup {
|
||||
@@ -25,7 +25,7 @@ export abstract class FilterGroup {
|
||||
this.updateView.emit();
|
||||
}
|
||||
|
||||
toggleOption(filter: Filter, option: FilterOption) {
|
||||
toggleOption(filter: Filter | RangeFilter, option: FilterOption) {
|
||||
const optionRef = this.getOptionRef(filter, option);
|
||||
if (optionRef) {
|
||||
optionRef.selected = !optionRef.selected;
|
||||
@@ -34,11 +34,11 @@ export abstract class FilterGroup {
|
||||
}
|
||||
}
|
||||
|
||||
getFilterRef(filter: Filter) {
|
||||
getFilterRef(filter: Filter | RangeFilter) {
|
||||
return this.value.find((f) => f.key === filter.key);
|
||||
}
|
||||
|
||||
getOptionRef(filter: Filter, option: FilterOption) {
|
||||
getOptionRef(filter: Filter | RangeFilter, option: FilterOption) {
|
||||
const filterRef = this.getFilterRef(filter);
|
||||
|
||||
if (filterRef) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type FilterType = 'select' | 'text' | 'date' | 'number' | 'checkbox';
|
||||
export type FilterType = 'select' | 'text' | 'date' | 'number' | 'checkbox' | 'integer-range';
|
||||
|
||||
@@ -6,3 +6,5 @@ export * from './select-option';
|
||||
export * from './filter';
|
||||
export * from './filter-type';
|
||||
export * from './select-filter-group';
|
||||
export * from './range-filter';
|
||||
export * from './range-filter-option';
|
||||
|
||||
9
apps/ui/filter/src/lib/models/range-filter-option.ts
Normal file
9
apps/ui/filter/src/lib/models/range-filter-option.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { FilterOptionBase } from './filter-option-base';
|
||||
|
||||
export interface RangeFilterOption extends FilterOptionBase {
|
||||
selected: boolean;
|
||||
value: string;
|
||||
minValue?: string | number;
|
||||
maxValue?: string | number;
|
||||
placeholder?: string;
|
||||
}
|
||||
8
apps/ui/filter/src/lib/models/range-filter.ts
Normal file
8
apps/ui/filter/src/lib/models/range-filter.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { FilterBase } from './filter-base';
|
||||
import { RangeFilterOption } from './range-filter-option';
|
||||
|
||||
export interface RangeFilter extends FilterBase<RangeFilterOption> {
|
||||
type: 'integer-range';
|
||||
target?: 'input' | 'filter';
|
||||
description?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user