src/app/modules/dashboard/components/filter/filter.component.ts
OnInit
OnDestroy
selector | app-filter |
styleUrls | ./filter.component.scss |
templateUrl | ./filter.component.html |
Properties |
|
Methods |
Inputs |
Outputs |
Accessors |
constructor(resourceService: ResourceService, fb: UntypedFormBuilder, activatedRoute: ActivatedRoute, cdr: ChangeDetectorRef, selectedDialogData: any)
|
||||||||||||||||||
Parameters :
|
chartData | |
Type : any
|
|
filters | |
Type : any
|
|
filterType | |
Type : string
|
|
hideElements | |
Type : boolean
|
|
Default value : false
|
|
resetFilters | |
Type : any
|
|
selectedFilter | |
Type : any
|
|
telemetryInteractObject | |
Type : IInteractEventObject
|
|
filterChanged | |
Type : EventEmitter<any>
|
|
autoCompleteChange | ||||||
autoCompleteChange(data, reference)
|
||||||
Parameters :
Returns :
void
|
buildFiltersForm |
buildFiltersForm()
|
Returns :
void
|
checkDependencyFilters |
checkDependencyFilters()
|
Returns :
void
|
checkFilterReferance | ||||
checkFilterReferance(element)
|
||||
Parameters :
Returns :
boolean
|
chooseOption |
chooseOption()
|
Returns :
void
|
filterData |
filterData()
|
Returns :
void
|
formGeneration | ||||
formGeneration(chartData)
|
||||
Parameters :
Returns :
void
|
formUpdate | ||||
formUpdate(chartData)
|
||||
Parameters :
Returns :
void
|
getDateRange | |||||
getDateRange(undefined, columnRef)
|
|||||
Parameters :
Returns :
void
|
getFilters | ||||
getFilters(options)
|
||||
Parameters :
Returns :
any
|
getFiltersValues | ||||
getFiltersValues(filter)
|
||||
Parameters :
Returns :
any
|
getSelectedData | ||||
getSelectedData(reference)
|
||||
Parameters :
Returns :
any
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
resetFilter |
resetFilter()
|
Returns :
void
|
setTelemetryInteractEdata | ||||
setTelemetryInteractEdata(val)
|
||||
Parameters :
Returns :
{ id: any; type: string; pageid: any; }
|
showErrorMessage | ||||
showErrorMessage(event)
|
||||
Parameters :
Returns :
void
|
Public activatedRoute |
Type : ActivatedRoute
|
chartConfig |
Type : any
|
chartLabels |
Type : any
|
Default value : []
|
currentReference |
Type : any
|
dateFilterReferenceName |
dateFilters |
Type : Array<string>
|
datepicker |
Type : ElementRef
|
Decorators :
@ViewChild('datePickerForFilters')
|
errorMessage |
Type : any
|
filterQuery |
Type : any
|
filtersFormGroup |
Type : UntypedFormGroup
|
filtersSubscription |
Type : Subscription
|
firstFilter |
Type : any
|
formChartData |
Type : any
|
Default value : []
|
loadash |
Default value : _
|
matAutocomplete |
Type : MatAutocomplete
|
Decorators :
@ViewChild('matAutocomplete')
|
noResultsFound |
Type : Boolean
|
pickerMaxDate |
Type : any
|
pickerMinDate |
Type : any
|
previousFilters |
Type : any
|
ranges |
Type : any
|
Default value : {
'Today': [dayjs(), dayjs()],
'Yesterday': [dayjs().subtract(1, 'days'), dayjs().subtract(1, 'days')],
'Last 7 Days': [dayjs().subtract(6, 'days'), dayjs()],
'Last 30 Days': [dayjs().subtract(29, 'days'), dayjs()],
'This Month': [dayjs().startOf('month'), dayjs().endOf('month')],
'Last Month': [dayjs().subtract(1, 'month').startOf('month'), dayjs().subtract(1, 'month').endOf('month')]
}
|
Public resourceService |
Type : ResourceService
|
resultStatistics |
Type : object
|
Default value : {}
|
Public selectedDialogData |
Type : any
|
Decorators :
@Inject(MAT_DIALOG_DATA)
|
selectedEndDate |
Type : any
|
selectedFilters |
Type : literal type
|
selectedStartDate |
Type : any
|
showFilters |
Type : Boolean
|
Default value : true
|
Public unsubscribe |
Default value : new Subject<void>()
|
selectedFilter | ||||||
setselectedFilter(val: any)
|
||||||
Parameters :
Returns :
void
|
resetFilters | ||||||
setresetFilters(val: any)
|
||||||
Parameters :
Returns :
void
|
import { Component, OnInit, EventEmitter, Input, Output, OnDestroy, ViewChild, ElementRef, ChangeDetectorRef, Inject} from '@angular/core';
import { IInteractEventObject } from '@sunbird/telemetry';
import { ResourceService} from '@sunbird/shared';
import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms';
import * as _ from 'lodash-es';
import dayjs from 'dayjs';
import { ActivatedRoute } from '@angular/router';
import { Subscription, Subject } from 'rxjs';
import { distinctUntilChanged, map, debounceTime, takeUntil } from 'rxjs/operators';
import { MatAutocomplete } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-filter',
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.scss']
})
export class FilterComponent implements OnInit, OnDestroy {
@Input() hideElements = false;
@Input() chartData: any;
@Input() filters: any;
@Input() telemetryInteractObject: IInteractEventObject;
@Output() filterChanged: EventEmitter<any> = new EventEmitter<any>();
@Input() filterType: string;
filtersFormGroup: UntypedFormGroup;
chartLabels: any = [];
chartConfig: any;
pickerMinDate: any; // min date that can be selected in the datepicker
pickerMaxDate: any; // max date that can be selected in datepicker
dateFilterReferenceName;
filtersSubscription: Subscription;
selectedFilters: {};
noResultsFound: Boolean;
resultStatistics = {};
selectedStartDate: any;
selectedEndDate: any;
loadash = _;
showFilters: Boolean = true;
dateFilters: Array<string>;
public unsubscribe = new Subject<void>();
previousFilters: any;
formChartData: any = [];
currentReference: any;
firstFilter: any;
errorMessage: any;
@Input()
set selectedFilter(val: any) {
if (val) {
this.selectedFilters = {};
if (val.filters) {
this.filters = val.filters;
}
this.formGeneration(val.data);
if (val.selectedFilters) {
this.selectedFilters = val.selectedFilters;
this.filtersFormGroup.setValue(val.selectedFilters);
}
}
}
@Input()
set resetFilters(val: any) {
if (val) {
const currentFilterValue = _.get(this.filtersFormGroup, 'value');
this.resetFilter();
this.chartData = val.data;
this.buildFiltersForm();
if (val.reset && val.reset == true) {
this.selectedFilters = {};
} else if (val.filters) {
this.filtersFormGroup.setValue(val.filters);
this.selectedFilters = val.filters;
} else if (currentFilterValue) {
this.filtersFormGroup.setValue(currentFilterValue);
this.selectedFilters = currentFilterValue;
}
}
}
ranges: any = {
'Today': [dayjs(), dayjs()],
'Yesterday': [dayjs().subtract(1, 'days'), dayjs().subtract(1, 'days')],
'Last 7 Days': [dayjs().subtract(6, 'days'), dayjs()],
'Last 30 Days': [dayjs().subtract(29, 'days'), dayjs()],
'This Month': [dayjs().startOf('month'), dayjs().endOf('month')],
'Last Month': [dayjs().subtract(1, 'month').startOf('month'), dayjs().subtract(1, 'month').endOf('month')]
};
@ViewChild('datePickerForFilters') datepicker: ElementRef;
@ViewChild('matAutocomplete') matAutocomplete: MatAutocomplete;
filterQuery: any;
constructor(
public resourceService: ResourceService,
private fb: UntypedFormBuilder,
public activatedRoute: ActivatedRoute,
private cdr: ChangeDetectorRef,
@Inject(MAT_DIALOG_DATA) public selectedDialogData: any
) {
}
ngOnInit() {
const charts = [];
if (this.chartData && this.chartData.length > 0) {
this.chartData.map(function(data) {
charts.push(...data.data);
return data.data;
});
}
this.formChartData = charts;
if (this.filters) {
this.buildFiltersForm();
}
}
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
formUpdate(chartData) {
const filterKeys = Object.keys(this.selectedFilters);
let previousKeys = [];
if (this.previousFilters) {
previousKeys = Object.keys(this.previousFilters);
}
_.forEach(this.filters, filter => {
const { reference } = filter;
const options = (_.sortBy(_.uniq(
_.map(chartData, (data) => (data && data[reference]) ? data[reference].toLowerCase() : ''
)))).filter(Boolean);
if(this.firstFilter && this.firstFilter[0] !== reference){
if (this.selectedFilters[reference] && this.selectedFilters[reference].length > 0) {
this.selectedFilters[reference] = options;
}
if (this.currentReference !== reference) {
filter.options = options;
}
}
if (!filterKeys.includes(reference)) {
filter.options = options;
}else if(previousKeys && previousKeys.includes(reference) && this.previousFilters && this.previousFilters[reference].length == this.selectedFilters[reference].length) {
if (options.length > filter.options) {
filter.options = options;
}
}
});
this.previousFilters = this.selectedFilters;
}
formGeneration(chartData) {
this.filtersFormGroup = this.fb.group({});
_.forEach(this.filters, filter => {
if (filter.controlType === 'date' || /date/i.test(_.get(filter, 'reference'))) {
const dateRange = _.uniq(_.map(chartData, _.get(filter, 'reference')));
this.pickerMinDate = dayjs(dateRange[0], 'DD-MM-YYYY');
this.pickerMaxDate = dayjs(dateRange[dateRange.length - 1], 'DD-MM-YYYY');
this.dateFilterReferenceName = filter.reference;
}
this.filtersFormGroup.addControl(_.get(filter, 'reference'), this.fb.control(''));
filter.options = (_.sortBy(_.uniq(
_.map(chartData, (data) => (data && data[filter.reference]) ? data[filter.reference].toLowerCase() : ''
)))).filter(Boolean);
});
}
buildFiltersForm() {
this.formGeneration(this.formChartData);
this.filtersSubscription = this.filtersFormGroup.valueChanges
.pipe(
takeUntil(this.unsubscribe),
map(filters => {
return _.omitBy(filters, _.isEmpty);
}),
debounceTime(100),
distinctUntilChanged()
)
.subscribe((filters) => {
this.selectedFilters = filters;
this.filterData();
}, (err) => {
});
if (this.chartData.selectedFilters) {
const tempSelectedFilters = {...this.chartData.selectedFilters}
setTimeout(() => {
for(let [key,value] of Object.entries(tempSelectedFilters)){
this.filtersFormGroup.controls[key].setValue(value)
}
},50)
}
}
setTelemetryInteractEdata(val) {
return {
id: _.join(_.split(val, ' '), '-').toLowerCase(),
type: 'click',
pageid: this.activatedRoute.snapshot.data.telemetry.pageid
};
}
resetFilter() {
if (this.filtersFormGroup) {
this.filtersFormGroup.reset();
this.selectedDialogData = {};
}
if (this.datepicker) {
this.datepicker.nativeElement.value = '';
}
this.selectedFilters = null;
this.firstFilter = null;
this.previousFilters = null;
this.showFilters = false;
this.cdr.detectChanges(); // to fix change detection issue in sui select
this.showFilters = true;
}
getDateRange({ startDate, endDate }, columnRef) {
this.selectedStartDate = dayjs(startDate).subtract(1, 'day');
this.selectedEndDate = dayjs(endDate);
const dateRange = [];
const dateDiff = this.selectedEndDate.diff(this.selectedStartDate, 'd');
for (let i = 0; i < dateDiff; i++) {
dateRange.push(dayjs(startDate).add(i, 'days').format('DD-MM-YYYY'))
}
this.filtersFormGroup.get(columnRef).setValue(dateRange);
}
checkDependencyFilters(){
_.map(this.filters, filter => {
const {reference,dependency} = filter;
if(dependency && !_.has(this.selectedFilters, `${dependency.reference}`) && _.has(this.selectedFilters, `${reference}`)){
this.filtersFormGroup.controls[reference].setValue('')
delete this.selectedFilters[reference]
}
})
}
filterData() {
if (this.selectedFilters) {
const filterKeys = Object.keys(this.selectedFilters);
if((!this.previousFilters || Object.keys(this.previousFilters).length === 0)&& Object.keys(this.selectedFilters).length === 1){
this.firstFilter = Object.keys(this.selectedFilters);
}
this.checkDependencyFilters();
if(this.firstFilter && this.firstFilter.length && !filterKeys.includes(this.firstFilter[0])){
this.chartData['selectedFilters'] = {};
this.filterChanged.emit({
allFilters: this.filters,
filters: {},
chartData: this.chartData,
});
this.showFilters = false;
this.resetFilter();
this.cdr.detectChanges();
this.showFilters = true;
return;
}
const filterData = [];
const filteredChartData = [];
this.chartData.forEach(chart => {
const id = chart.id;
delete chart?.id;
delete chart?.data?.selectedFilters;
delete chart?.data?.id;
const result: Array<{}> = _.filter(chart.data, data => {
return _.every(this.selectedFilters, (filterValues, key) => {
if (data && data[key]) {
return _.some(filterValues, filterValue => _.trim(_.toLower(filterValue)) === _.trim(_.toLower(_.get(data, key))));
}
});
});
filteredChartData.push({ id: id, data: result });
result['selectedFilters'] = this.selectedFilters;
filterData.push(...result);
});
this.formUpdate(filterData);
const keys = Object.keys(this.selectedFilters);
this.dateFilters = [];
this.filters.map(ele => {
if (ele && ele['controlType'].toLowerCase() == 'date') {
keys.map(item => {
if (item == ele['reference']) {
this.dateFilters.push(item);
}
});
}
});
this.filterChanged.emit({
allFilters: this.filters,
filters: this.selectedFilters,
chartData: filteredChartData,
});
} else {
this.dateFilters = [];
this.filterChanged.emit({
allFilters: this.filters,
filters: {},
chartData: this.chartData,
});
}
}
checkFilterReferance(element) {
if (this.dateFilters && this.dateFilters.includes(element)) {
return true;
} else {
return false;
}
}
autoCompleteChange(data, reference) {
const object = {};
if (data && data.length > 0) {
object[reference] = data;
this.currentReference = reference;
}
if (this.selectedDialogData) {
for (const key in this.selectedDialogData) {
if (key != reference) {
this.filtersFormGroup.controls[key].setValue(this.selectedDialogData[key]);
}
}
}
this.filtersFormGroup.controls[reference].setValue(data);
}
getSelectedData(reference) {
if (Object.keys(this.selectedDialogData).length && this.selectedDialogData[reference]) {
return this.selectedDialogData[reference];
} else if (this.selectedFilters && this.selectedFilters[reference]) {
return this.selectedFilters[reference];
} else {
return [];
}
}
getFilters(options) {
if (this.filterQuery && this.filterQuery != '') {
return options.filter(opt =>
opt.toLowerCase().indexOf(this.filterQuery.toLowerCase()) === 0);
} else {
return options;
}
}
chooseOption(): void {
this.matAutocomplete.options.first.select();
}
getFiltersValues(filter) {
return Array.isArray(filter) ? filter : [filter];
}
showErrorMessage(event){
const regex = /{displayName}/g
this.errorMessage = event?.displayName ? this.resourceService?.frmelmnts?.lbl?.selectDependentFilter.replace(regex, event.displayName): undefined;
}
}
<form [formGroup]="filtersFormGroup" class="sb-form" [ngClass]="{'hide': hideElements}"
*ngIf="showFilters && !hideElements">
<div content>
<div>
<div class="sb-prominent-filter-container pb-0 d-flex flex-w-wrap flex-ai-center">
<div class="sb-prominent-filter-field mr-8" *ngFor="let filter of filters">
<label>{{filter.displayName}}</label>
<div *ngIf="filter.controlType === 'multi-select'">
<app-material-auto-complete name="mat-ms-auto" [dynamicplaceholder]="filter.displayName" [dropdownList]="filter.options"
[selectedFilters]="getSelectedData(filter.reference)"
(selectionChanged)="autoCompleteChange($event,filter.reference)" [dependency]="filter.dependency" [checkFilters]="selectedFilters" display-key="name" (errorOutput)="showErrorMessage($event)">
</app-material-auto-complete>
</div>
<div *ngIf="filter.controlType === 'select'">
<input matInput type="text" [(ngModel)]="filterQuery" (keyup.enter)="chooseOption()"
placeholder="Select {{filter.displayName | lowercase}}" aria-label="Number" matInput [formControlName]="filter.reference"
[matAutocomplete]="auto2">
<mat-autocomplete #auto2="matAutocomplete" class="test-class">
<mat-option *ngFor="let option of getFilters(filter.options)" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
</div>
<div *ngIf="filter.controlType === 'date'">
<input appTelemetryInteract [telemetryInteractObject]="telemetryInteractObject"
[telemetryInteractEdata]="setTelemetryInteractEdata('date-filter')"
[telemetryInteractCdata]="telemetryCdata" #datePickerForFilters class="sb-form-control" type="text"
placeholder="Select {{filter.displayName | lowercase}}" ngxDaterangepickerMd [showCustomRangeLabel]="true"
[locale]="datePickerConfig" [alwaysShowCalendars]="true" [ranges]="ranges" [linkedCalendars]="true"
[minDate]="pickerMinDate" [maxDate]="pickerMaxDate" (change)="getDateRange($event,filter.reference)" />
</div>
</div>
</div>
</div>
</div>
</form>
<div *ngIf="errorMessage" class="errorMessage">*{{errorMessage}}</div>
<div class="sb-filter-label pt-16 pb-8" *ngIf="selectedFilters && filterType=='report-filter'">
<div *ngFor="let key of loadash.keys(selectedFilters)" class="d-inline-flex flex-w-wrap pr-10">
<span *ngIf="checkFilterReferance(key)">
<span class="sb-label-name mb-4">{{key}}:</span>
<span class="date-range-label" *ngIf="selectedFilters[key]?.length>0">
{{selectedFilters[key][0]}} - {{selectedFilters[key][selectedFilters[key].length-1]}}
</span>
</span>
<span *ngIf="!checkFilterReferance(key)">
<span class="sb-label-name mb-4">{{key}}:</span><span class="sb-label-value"
*ngFor="let val of getFiltersValues(selectedFilters[key])">{{val}}
</span>
</span>
</div>
</div>
./filter.component.scss
@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;
.sb-prominent-filter-field {
.form-control {
&:focus {
border: calculateRem(1px) solid var(--rc-dddddd);
border-radius: calculateRem(24px);
}
&::placeholder {
color: var(--black);
opacity: 1;
}
&:-ms-input-placeholder {
color: var(--black);
}
&::-ms-input-placeholder {
color: var(--black);
}
}
.md-drppicker .ranges ul li {
margin-bottom: calculateRem(8px);
}
}
.sb-prominent-filter
.sb-prominent-filter-container
.sb-prominent-filter-field:last-child {
margin-left: 0;
margin-top: 0;
text-align: left;
padding: 0px;
padding-right: calculateRem(16px);
}
.sb-prominent-filter
.sb-prominent-filter-container
.sb-prominent-filter-field {
margin-bottom: calculateRem(24px);
}
.reset-filter-section {
display: block !important;
position: relative;
right: 0;
top: calculateRem(2px);
align-items: center;
.selection {
padding: calculateRem(8px);
}
}
.sb-filter-label {
border-bottom: 0px;
width: 100%;
.sb-label-value {
border-radius: calculateRem(24px);
padding: calculateRem(7px) !important;
padding-left: calculateRem(9px) !important;
padding-right: calculateRem(9px) !important;
background-color: var(---rc-e9e8d9) !important;
box-shadow: calculateRem(4px) calculateRem(4px) calculateRem(3px) 0 var(--gray-100) !important;
color: var(--gray-800);
margin-bottom: calculateRem(4px);
margin-right: calculateRem(4px);
}
.sb-label-name {
font-size: calculateRem(16px);
}
}
:host ::ng-deep {
.ui.toggle.checkbox input:checked ~ label,
.ui.checkbox input.hidden + label {
margin-bottom: 0px;
}
.ui.toggle.checkbox .box:before,
.ui.toggle.checkbox label:before {
background: rgba(var(--rc-rgba-black), 0.2);
}
.ui.toggle.checkbox .box:after,
.ui.toggle.checkbox label:after {
width: 1.5rem;
height: 1.2rem;
top: calculateRem(2px);
}
.ui.toggle.checkbox input ~ .box:after,
.ui.toggle.checkbox input ~ label:after {
left: 0.1rem;
}
.ui.toggle.checkbox input:checked ~ .box:after,
.ui.toggle.checkbox input:checked ~ label:after {
left: 1.8rem;
}
.ui.styled.accordion,
.ui.styled.accordion .accordion {
box-shadow: none;
border-bottom: calculateRem(1px) solid var(--rc-dddddd);
}
.graph-filters {
.ui.form {
min-height: calculateRem(36px);
}
.ui.styled.accordion .accordion .title,
.ui.styled.accordion .title {
padding-left: 0;
padding-right: 0;
}
.ui.styled.accordion .accordion .content,
.ui.styled.accordion .content {
padding: 0;
}
.ui.styled.accordion,
.ui.styled.accordion .accordion {
box-shadow: none;
border-bottom: calculateRem(1px) solid var(--rc-dddddd);
}
}
}
.sb-prominent-filter-field {
::ng-deep {
label {
padding-bottom: calculateRem(2px);
margin-bottom: 0px !important;
margin-left: calculateRem(10px) !important;
}
.md-drppicker {
padding: calculateRem(16px) calculateRem(8px);
.ranges ul li {
margin-bottom: calculateRem(8px);
}
.btn {
background-color: var(--primary-400);
margin-right: calculateRem(16px);
line-height: calculateRem(32px);
}
.buttons {
margin: 0;
}
}
}
}
.ui.dropdown.selection
::ng-deep {
.menu {
padding-top: calculateRem(16px);
border-radius: calculateRem(16px) !important;
}
}
.ui.form {
::ng-deep {
sui-accordion {
sui-accordion-panel {
.title {
padding-top: calculateRem(8px) !important;
}
}
}
}
}
.ui.selection.dropdown {
color: var(--ui-dropdown-selection-icon) !important;
box-sizing: border-box;
height: calculateRem(40px) !important;
border: calculateRem(1px) solid var(--rc-e2e3e5);
border-radius: calculateRem(24px);
background-color: var(--white);
}
.ui.selection.dropdown {
::ng-deep {
.text {
font-size: calculateRem(18px) !important;
}
}
}
sui-multi-select.selection.sb-select-searchfilter.ui.dropdown.multiple {
min-width: calculateRem(250px) !important;
min-height: calculateRem(40px) !important;
font-size: calculateRem(16px) !important;
}
.sb-prominent-filter {
background: var(---rc-e9e8d9) !important;
}
.errorMessage{
color:var(--ck-color-base-error);
margin-top: 0.625rem;
}
::ng-deep{
.modals.dimmer .ui.scrolling.modal {
margin: 3.5rem auto auto !important;
}
}
.additional-selection {
opacity: 0.75;
font-size: 0.75em;
}
.select-container{
margin-top:calculateRem(10px);
}