mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
@@ -1,15 +1,22 @@
|
||||
import { computed, inject, Injectable, signal } from '@angular/core';
|
||||
import { computed, inject, Injectable, Input, signal } from '@angular/core';
|
||||
import { InputType } from '../types';
|
||||
import { getState, patchState, signalState } from '@ngrx/signals';
|
||||
import { mapToFilter } from './mappings';
|
||||
import { isEqual } from 'lodash';
|
||||
import { FilterInput, OrderByDirectionSchema, Query, QuerySchema } from './schemas';
|
||||
import {
|
||||
FilterInput,
|
||||
OrderByDirectionSchema,
|
||||
Query,
|
||||
QuerySchema,
|
||||
} from './schemas';
|
||||
import { FILTER_ON_COMMIT, FILTER_ON_INIT, QUERY_SETTINGS } from './tokens';
|
||||
|
||||
@Injectable()
|
||||
export class FilterService {
|
||||
#onInit = inject(FILTER_ON_INIT, { optional: true })?.map((fn) => fn(this));
|
||||
#onCommit = inject(FILTER_ON_COMMIT, { optional: true })?.map((fn) => fn(this));
|
||||
#onCommit = inject(FILTER_ON_COMMIT, { optional: true })?.map((fn) =>
|
||||
fn(this),
|
||||
);
|
||||
|
||||
readonly settings = inject(QUERY_SETTINGS);
|
||||
|
||||
@@ -72,7 +79,11 @@ export class FilterService {
|
||||
this.setOrderBy({ by, dir: orderByDir }, options);
|
||||
}
|
||||
|
||||
setInputTextValue(key: string, value: string | undefined, options?: { commit: boolean }): void {
|
||||
setInputTextValue(
|
||||
key: string,
|
||||
value: string | undefined,
|
||||
options?: { commit: boolean },
|
||||
): void {
|
||||
const inputs = this.#state.inputs().map((input) => {
|
||||
if (input.key !== key) {
|
||||
return input;
|
||||
@@ -94,7 +105,11 @@ export class FilterService {
|
||||
}
|
||||
}
|
||||
|
||||
setInputCheckboxValue(key: string, selected: string[], options?: { commit: boolean }): void {
|
||||
setInputCheckboxValue(
|
||||
key: string,
|
||||
selected: string[],
|
||||
options?: { commit: boolean },
|
||||
): void {
|
||||
const inputs = this.#state.inputs().map((input) => {
|
||||
if (input.key !== key) {
|
||||
return input;
|
||||
@@ -116,6 +131,33 @@ export class FilterService {
|
||||
}
|
||||
}
|
||||
|
||||
setInputDateRangeValue(
|
||||
key: string,
|
||||
start?: string,
|
||||
stop?: string,
|
||||
options?: { commit: boolean },
|
||||
): void {
|
||||
const inputs = this.#state.inputs().map((input) => {
|
||||
if (input.key !== key) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (input.type === InputType.DateRange) {
|
||||
return { ...input, start, stop };
|
||||
}
|
||||
|
||||
console.warn(`Input type not supported: ${input.type}`);
|
||||
|
||||
return input;
|
||||
});
|
||||
|
||||
patchState(this.#state, { inputs });
|
||||
|
||||
if (options?.commit) {
|
||||
this.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the current state is the default state.
|
||||
* This computed property checks if the current state is equal to the default state.
|
||||
@@ -126,8 +168,12 @@ export class FilterService {
|
||||
});
|
||||
|
||||
isDefaultFilterInput(filterInput: FilterInput) {
|
||||
const currentInputState = this.#state.inputs().find((i) => i.key === filterInput.key);
|
||||
const defaultInputState = this.defaultState.inputs.find((i) => i.key === filterInput.key);
|
||||
const currentInputState = this.#state
|
||||
.inputs()
|
||||
.find((i) => i.key === filterInput.key);
|
||||
const defaultInputState = this.defaultState.inputs.find(
|
||||
(i) => i.key === filterInput.key,
|
||||
);
|
||||
|
||||
return isEqual(currentInputState, defaultInputState);
|
||||
}
|
||||
@@ -146,14 +192,20 @@ export class FilterService {
|
||||
return !input.selected?.length;
|
||||
}
|
||||
|
||||
console.warn(`Input type not supported: ${input.type}`);
|
||||
if (input.type === InputType.DateRange) {
|
||||
return !input.start && !input.stop;
|
||||
}
|
||||
|
||||
console.warn(`Input type not supported`);
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
isEmptyFilterInput(filterInput: FilterInput) {
|
||||
const currentInputState = this.#state.inputs().find((i) => i.key === filterInput.key);
|
||||
const currentInputState = this.#state
|
||||
.inputs()
|
||||
.find((i) => i.key === filterInput.key);
|
||||
|
||||
if (currentInputState?.type === InputType.Text) {
|
||||
return !currentInputState.value;
|
||||
@@ -163,7 +215,11 @@ export class FilterService {
|
||||
return !currentInputState.selected?.length;
|
||||
}
|
||||
|
||||
console.warn(`Input type not supported: ${currentInputState?.type}`);
|
||||
if (currentInputState?.type === InputType.DateRange) {
|
||||
return !currentInputState.start && !currentInputState.stop;
|
||||
}
|
||||
|
||||
console.warn(`Input type not supported`);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -226,7 +282,9 @@ export class FilterService {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputIndex = this.#commitdState().inputs.findIndex((i) => i.key === key);
|
||||
const inputIndex = this.#commitdState().inputs.findIndex(
|
||||
(i) => i.key === key,
|
||||
);
|
||||
|
||||
if (inputIndex === -1) {
|
||||
console.warn(`No committed input found with key: ${key}`);
|
||||
@@ -243,7 +301,9 @@ export class FilterService {
|
||||
|
||||
commitOrderBy() {
|
||||
const orderBy = this.#state.orderBy().map((o) => {
|
||||
const committedOrderBy = this.#commitdState().orderBy.find((co) => co.by === o.by);
|
||||
const committedOrderBy = this.#commitdState().orderBy.find(
|
||||
(co) => co.by === o.by,
|
||||
);
|
||||
return { ...o, dir: committedOrderBy?.dir };
|
||||
});
|
||||
|
||||
@@ -260,6 +320,10 @@ export class FilterService {
|
||||
return { ...input, selected: [] };
|
||||
}
|
||||
|
||||
if (input.type === InputType.DateRange) {
|
||||
return { ...input, start: undefined, stop: undefined };
|
||||
}
|
||||
|
||||
return input;
|
||||
});
|
||||
|
||||
@@ -355,6 +419,15 @@ export class FilterService {
|
||||
result[input.key] = input.selected.join(';');
|
||||
}
|
||||
break;
|
||||
case InputType.DateRange:
|
||||
if (input.start && input.stop) {
|
||||
result[input.key] = `"${input.start}"-"${input.stop}"`;
|
||||
} else if (input.start) {
|
||||
result[input.key] = `"${input.start}"-`;
|
||||
} else if (input.stop) {
|
||||
result[input.key] = `-"${input.stop}"`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +442,9 @@ export class FilterService {
|
||||
|
||||
isQueryParamsEqual(params: Record<string, string>): boolean {
|
||||
const currentParams = this.queryParams();
|
||||
return this.queryParamKeys().every((key) => params[key] === currentParams[key]);
|
||||
return this.queryParamKeys().every(
|
||||
(key) => params[key] === currentParams[key],
|
||||
);
|
||||
}
|
||||
|
||||
queryParamKeys = computed(() => {
|
||||
@@ -396,6 +471,14 @@ export class FilterService {
|
||||
acc[input.key] = input.value || '';
|
||||
} else if (input.type === InputType.Checkbox) {
|
||||
acc[input.key] = input.selected?.join(';') || '';
|
||||
} else if (input.type === InputType.DateRange) {
|
||||
if (input.start && input.stop) {
|
||||
acc[input.key] = `"${input.start}"-"${input.stop}"`;
|
||||
} else if (input.start) {
|
||||
acc[input.key] = `"${input.start}"-`;
|
||||
} else if (input.stop) {
|
||||
acc[input.key] = `-"${input.stop}"`;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
@@ -404,6 +487,14 @@ export class FilterService {
|
||||
acc[input.key] = input.value || '';
|
||||
} else if (input.type === InputType.Checkbox) {
|
||||
acc[input.key] = input.selected?.join(';') || '';
|
||||
} else if (input.type === InputType.DateRange) {
|
||||
if (input.start && input.stop) {
|
||||
acc[input.key] = `"${input.start}"-"${input.stop}"`;
|
||||
} else if (input.start) {
|
||||
acc[input.key] = `"${input.start}"-`;
|
||||
} else if (input.stop) {
|
||||
acc[input.key] = `-"${input.stop}"`;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
@@ -418,7 +509,10 @@ export class FilterService {
|
||||
});
|
||||
});
|
||||
|
||||
parseQueryParams(params: Record<string, string>, options?: { commit: boolean }): void {
|
||||
parseQueryParams(
|
||||
params: Record<string, string>,
|
||||
options?: { commit: boolean },
|
||||
): void {
|
||||
this.reset();
|
||||
for (const key in params) {
|
||||
if (key === 'orderBy') {
|
||||
@@ -440,6 +534,14 @@ export class FilterService {
|
||||
case InputType.Checkbox:
|
||||
this.setInputCheckboxValue(key, params[key].split(';'));
|
||||
break;
|
||||
case InputType.DateRange: {
|
||||
const decoded = decodeURIComponent(params[key]);
|
||||
const [startRaw, stopRaw] = decoded.split('-"');
|
||||
const start = startRaw?.replace(/"/g, '') || undefined;
|
||||
const stop = stopRaw?.replace(/"/g, '') || undefined;
|
||||
this.setInputDateRangeValue(key, start, stop);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn(`Input type not supported: ${inputType}`);
|
||||
break;
|
||||
|
||||
@@ -22,9 +22,7 @@ import { InputType } from '../../types';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
standalone: true,
|
||||
imports: [RangeDatepickerComponent, ReactiveFormsModule],
|
||||
host: {
|
||||
'[class]': "['filter-datepicker-range-input']",
|
||||
},
|
||||
host: { '[class]': "['filter-datepicker-range-input']" },
|
||||
})
|
||||
export class DatepickerRangeInputComponent {
|
||||
readonly filterService = inject(FilterService);
|
||||
@@ -36,19 +34,14 @@ export class DatepickerRangeInputComponent {
|
||||
input = computed<DateRangeFilterInput>(() => {
|
||||
const inputs = this.filterService.inputs();
|
||||
const input = inputs.find(
|
||||
(input) => input.key === this.inputKey() && input.type === InputType.DateRange,
|
||||
(input) =>
|
||||
input.key === this.inputKey() && input.type === InputType.DateRange,
|
||||
) as DateRangeFilterInput;
|
||||
|
||||
if (!input) {
|
||||
throw new Error(`Input not found for key: ${this.inputKey()}`);
|
||||
}
|
||||
|
||||
const startDate = input.start ? new Date(input.start) : undefined;
|
||||
const stopDate = input.stop ? new Date(input.stop) : undefined;
|
||||
|
||||
this.datepicker.setValue([startDate, stopDate]);
|
||||
this.datepicker.updateValueAndValidity();
|
||||
|
||||
return input;
|
||||
});
|
||||
|
||||
@@ -63,12 +56,38 @@ export class DatepickerRangeInputComponent {
|
||||
});
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const input = this.input();
|
||||
const startDate = input.start ? new Date(input.start) : undefined;
|
||||
const stopDate = input.stop ? new Date(input.stop) : undefined;
|
||||
|
||||
this.datepicker.patchValue([startDate, stopDate]);
|
||||
this.datepicker.updateValueAndValidity();
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
this.valueChanges();
|
||||
untracked(() => {
|
||||
console.log('Datepicker Value Changes', {
|
||||
values: this.datepicker?.value,
|
||||
});
|
||||
const startDate = this.datepicker?.value?.[0];
|
||||
const stopDate = this.datepicker?.value?.[1];
|
||||
|
||||
if (!startDate && !stopDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const start = startDate?.toISOString();
|
||||
const stop = stopDate?.toISOString();
|
||||
|
||||
const controlEqualsInput =
|
||||
this.input().start === start && this.input().stop === stop;
|
||||
|
||||
if (!controlEqualsInput) {
|
||||
this.filterService.setInputDateRangeValue(
|
||||
this.inputKey(),
|
||||
start,
|
||||
stop,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { format, isValid, parse, isEqual } from 'date-fns';
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
standalone: true,
|
||||
})
|
||||
// TODO: Date Input Directive soll ausgelagert werden in eine eigene Lib
|
||||
export class DateInputDirective implements ControlValueAccessor {
|
||||
|
||||
Reference in New Issue
Block a user