File

src/app/modules/dashboard/components/filter/filter.component.ts

Implements

OnInit OnDestroy

Metadata

Index

Properties
Methods
Inputs
Outputs
Accessors

Constructor

constructor(resourceService: ResourceService, fb: UntypedFormBuilder, activatedRoute: ActivatedRoute, cdr: ChangeDetectorRef, selectedDialogData: any)
Parameters :
Name Type Optional
resourceService ResourceService No
fb UntypedFormBuilder No
activatedRoute ActivatedRoute No
cdr ChangeDetectorRef No
selectedDialogData any No

Inputs

chartData
Type : any
filters
Type : any
filterType
Type : string
hideElements
Type : boolean
Default value : false
resetFilters
Type : any
selectedFilter
Type : any
telemetryInteractObject
Type : IInteractEventObject

Outputs

filterChanged
Type : EventEmitter<any>

Methods

autoCompleteChange
autoCompleteChange(data, reference)
Parameters :
Name Optional
data No
reference No
Returns : void
buildFiltersForm
buildFiltersForm()
Returns : void
checkDependencyFilters
checkDependencyFilters()
Returns : void
checkFilterReferance
checkFilterReferance(element)
Parameters :
Name Optional
element No
Returns : boolean
chooseOption
chooseOption()
Returns : void
filterData
filterData()
Returns : void
formGeneration
formGeneration(chartData)
Parameters :
Name Optional
chartData No
Returns : void
formUpdate
formUpdate(chartData)
Parameters :
Name Optional
chartData No
Returns : void
getDateRange
getDateRange(undefined, columnRef)
Parameters :
Name Optional
No
columnRef No
Returns : void
getFilters
getFilters(options)
Parameters :
Name Optional
options No
Returns : any
getFiltersValues
getFiltersValues(filter)
Parameters :
Name Optional
filter No
Returns : any
getSelectedData
getSelectedData(reference)
Parameters :
Name Optional
reference No
Returns : any
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
resetFilter
resetFilter()
Returns : void
setTelemetryInteractEdata
setTelemetryInteractEdata(val)
Parameters :
Name Optional
val No
showErrorMessage
showErrorMessage(event)
Parameters :
Name Optional
event No
Returns : void

Properties

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>()

Accessors

selectedFilter
setselectedFilter(val: any)
Parameters :
Name Type Optional
val any No
Returns : void
resetFilters
setresetFilters(val: any)
Parameters :
Name Type Optional
val any No
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);
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""