File

src/app/modules/content-search/components/global-search-filter/global-search-filter.component.ts

Implements

OnInit OnChanges OnDestroy

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(resourceService: ResourceService, router: Router, activatedRoute: ActivatedRoute, cdr: ChangeDetectorRef, utilService: UtilService, userService: UserService, cacheService: CacheService, connectionService: ConnectionService)
Parameters :
Name Type Optional
resourceService ResourceService No
router Router No
activatedRoute ActivatedRoute No
cdr ChangeDetectorRef No
utilService UtilService No
userService UserService No
cacheService CacheService No
connectionService ConnectionService No

Inputs

cachedFilters
Type : any
facets
Type : any
isOpen
Type : any
layoutConfiguration
Type : any
queryParamsToOmit
Type : any
supportedFilterAttributes
Type : {}
Default value : ['se_boards', 'se_mediums', 'se_gradeLevels', 'se_subjects', 'primaryCategory', 'mediaType', 'additionalCategories', 'channel']

Outputs

filterChange
Type : EventEmitter<literal type>

Methods

Private emitFilterChangeEvent
emitFilterChangeEvent(skipUrlUpdate)
Parameters :
Name Optional Default value
skipUrlUpdate No false
Returns : void
Private fetchSelectedFilterAndFilterOption
fetchSelectedFilterAndFilterOption()
Returns : void
Private handleFilterChange
handleFilterChange()
Returns : void
Private hardRefreshFilter
hardRefreshFilter()
Returns : void
ngOnChanges
ngOnChanges(changes: SimpleChanges)
Parameters :
Name Type Optional
changes SimpleChanges No
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onChange
onChange(facet)
Parameters :
Name Optional
facet No
Returns : void
onSearchFacetFilterReset
onSearchFacetFilterReset()
Returns : void
removeFilterSelection
removeFilterSelection(data)
Parameters :
Name Optional
data No
Returns : void
Public resetFilters
resetFilters()
Returns : void
Private setResetFilterInteractData
setResetFilterInteractData()
Returns : void
Public updateRoute
updateRoute()
Returns : void
updateTerms
updateTerms()
Returns : void

Properties

Public connectionService
Type : ConnectionService
Public filterChangeEvent
Default value : new Subject()
Optional filterFormTemplateConfig
Type : IFacetFilterFieldTemplateConfig[]
Public filterLayout
Default value : LibraryFiltersLayout
Public isConnected
Default value : true
Public refresh
Default value : true
Public resetFilterInteractEdata
Type : IInteractEventEdata
Public resourceService
Type : ResourceService
Public router
Type : Router
searchFacetFilterComponent
Type : any
Decorators :
@ViewChild('sbSearchFacetFilterComponent')
Public selectedFilters
Type : any
Default value : {}
Public selectedMediaType
Type : string
Public selectedMediaTypeIndex
Type : number
Default value : 0
Private unsubscribe$
Default value : new Subject<void>()
Public userService
Type : UserService
import {
  Component,
  Output,
  EventEmitter,
  Input,
  OnInit,
  OnDestroy,
  ChangeDetectorRef,
  OnChanges,
  SimpleChanges, ViewChild
} from '@angular/core';
import * as _ from 'lodash-es';
import { ResourceService, UtilService, ConnectionService } from '@sunbird/shared';
import { IInteractEventEdata } from '@sunbird/telemetry';
import { Router, ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { debounceTime, map, takeUntil, filter } from 'rxjs/operators';
import { LibraryFiltersLayout } from '@project-sunbird/common-consumption';
import { UserService } from '@sunbird/core';
import { IFacetFilterFieldTemplateConfig } from '@project-sunbird/common-form-elements-full';
import { CacheService } from '../../../shared/services/cache-service/cache.service';

@Component({
  selector: 'app-global-search-filter',
  templateUrl: './global-search-filter.component.html',
  styleUrls: ['./global-search-filter.component.scss']
})
export class GlobalSearchFilterComponent implements OnInit, OnChanges, OnDestroy {
  @Input() facets;
  @Input() queryParamsToOmit;
  @Input() supportedFilterAttributes = ['se_boards', 'se_mediums', 'se_gradeLevels', 'se_subjects', 'primaryCategory', 'mediaType', 'additionalCategories', 'channel'];
  public filterLayout = LibraryFiltersLayout;
  public selectedMediaTypeIndex = 0;
  public selectedMediaType: string;
  public selectedFilters: any = {};
  public refresh = true;
  public isConnected = true;
  public filterChangeEvent = new Subject();
  private unsubscribe$ = new Subject<void>();
  public resetFilterInteractEdata: IInteractEventEdata;
  @Input() layoutConfiguration;
  @Input() isOpen;
  @Output() filterChange: EventEmitter<{ status: string, filters?: any }> = new EventEmitter();
  @Input() cachedFilters?: any;

  @ViewChild('sbSearchFacetFilterComponent') searchFacetFilterComponent: any;

  filterFormTemplateConfig?: IFacetFilterFieldTemplateConfig[];

  constructor(public resourceService: ResourceService, public router: Router,
    private activatedRoute: ActivatedRoute, private cdr: ChangeDetectorRef, private utilService: UtilService,
    public userService: UserService, private cacheService: CacheService, public connectionService: ConnectionService) {
  }

  onChange(facet) {
    let channelData;
    if (this.selectedFilters.channel) {
      const channelIds = [];
      const facetsData = _.find(this.facets, {'name': 'channel'});
      _.forEach(this.selectedFilters.channel, (value, index) => {
        channelData = _.find(facetsData.values, {'identifier': value});
        if (!channelData) {
          channelData = _.find(facetsData.values, {'name': value});
        }
        channelIds.push(channelData.name);
      });
      this.selectedFilters.channel = channelIds;
    }
    this.filterChangeEvent.next({event: this.selectedFilters[facet.name], type: facet.name});
  }

  ngOnChanges(changes: SimpleChanges) {
    if (_.get(changes, 'facets.currentValue.length')) {
      const updatedFacets = changes['facets'].currentValue;

      this.filterFormTemplateConfig = [...updatedFacets].sort((a, b) => {
        if (a.index && b.index) {
          return a.index.localeCompare(b.index);
        }
        if (a.index) {
          return 1;
        }
        return -1;
      }).map((f) => {
        if (f.name === 'mediaType') {
          f.values = f.mimeTypeList.map((m) => ({name: m}));

          return {
            facet: f.name,
            type: 'pills',
            labelText: f.label || f.name,
            placeholderText: `${this.resourceService.frmelmnts.lbl.Select} ${f.label || f.name}`,
            multiple: true
          };
        }

        return {
          facet: f.name,
          type: 'dropdown',
          labelText: f.label || f.name,
          placeholderText: `${this.resourceService.frmelmnts.lbl.Select} ${f.label || f.name}`,
          multiple: true
        };
      });
      this.resourceService.languageSelected$.pipe(takeUntil(this.unsubscribe$)).subscribe((languageData) => {
        this.filterFormTemplateConfig.forEach((facet) => {
          facet['labelText'] = this.utilService.transposeTerms(facet['labelText'], facet['labelText'], this.resourceService.selectedLang);
          facet['placeholderText'] = this.utilService.transposeTerms(facet['placeholderText'], facet['placeholderText'], this.resourceService.selectedLang);
        });
      });
    }
  }

  ngOnInit() {
    this.setResetFilterInteractData();
    this.fetchSelectedFilterAndFilterOption();
    this.handleFilterChange();
        // screen size
        if (window.innerWidth <= 992 ) {
          this.isOpen = false;
        }
    this.connectionService.monitor().subscribe(isConnected => {
      this.isConnected = isConnected;
    });
  }

  public resetFilters() {
    this.selectedFilters = _.pick(this.selectedFilters, ['key', 'selectedTab']);
    if (this.utilService.isDesktopApp) {
      const userPreferences: any = this.userService.anonymousUserPreference;
      if (userPreferences) {
        _.forEach(['board', 'medium', 'gradeLevel', 'channel'], (item) => {
          if (!_.has(this.selectedFilters, item)) {
            this.selectedFilters[item] = _.isArray(userPreferences.framework[item]) ?
            userPreferences.framework[item] : _.split(userPreferences.framework[item], ', ');
          }
        });
      }
    }
    let queryFilters = _.get(this.activatedRoute, 'snapshot.queryParams');
    let redirectUrl; // if pageNumber exist then go to first page every time when filter changes, else go exact path
    if (_.get(this.activatedRoute, 'snapshot.params.pageNumber')) { // when using dataDriven filter should this should be verified
      redirectUrl = this.router.url.split('?')[0].replace(/[^\/]+$/, '1');
    } else {
      redirectUrl = this.router.url.split('?')[0];
    }
    if (this.queryParamsToOmit) {
      queryFilters = _.omit(_.get(this.activatedRoute, 'snapshot.queryParams'), this.queryParamsToOmit);
      queryFilters = {...queryFilters, ...this.selectedFilters};
    }
    redirectUrl = decodeURI(redirectUrl);
    this.router.navigate([redirectUrl], {
      relativeTo: this.activatedRoute.parent, queryParams: this.queryParamsToOmit ? queryFilters : this.selectedFilters
    });
    this.hardRefreshFilter();
  }

  private fetchSelectedFilterAndFilterOption() {
    this.activatedRoute.queryParams.pipe(takeUntil(this.unsubscribe$),
      map((queryParams) => {
        const queryFilters: any = {};
        _.forIn(queryParams, (value, key) => {
          if (['medium', 'gradeLevel', 'board', 'channel', 'subject', 'primaryCategory', 'key', 'mediaType', 'se_boards', 'se_mediums', 'se_gradeLevels', 'se_subjects', 'additionalCategories'].includes(key)) {
            queryFilters[key] = key === 'key' || _.isArray(value) ? value : [value];
          }
        });
        if(_.get(queryParams,'ignoreSavedFilter')){
          queryFilters['ignoreSavedFilter'] = queryParams.ignoreSavedFilter;
        }
        if (queryParams.selectedTab) {
          queryFilters['selectedTab'] = queryParams.selectedTab;
        }
        if (queryParams.mediaType) {
          this.selectedMediaType = _.isArray(queryParams.mediaType) ? queryParams.mediaType[0] : queryParams.mediaType;
        } else {
          this.selectedMediaType = '';
          this.selectedMediaTypeIndex = 0;
        }
        return queryFilters;
      })).subscribe(filters => {
        if (_.get(filters, 'key') && _.get(filters, 'ignoreSavedFilter') !== 'true') {
          this.cacheService.remove('searchFiltersAll');
        }
        if (this.cacheService.exists('searchFiltersAll') && !_.get(filters, 'key') &&
        _.get(filters, 'ignoreSavedFilter') !== 'true') {
          this.selectedFilters = _.cloneDeep(this.cacheService.get('searchFiltersAll'));
        } else {
          if( _.get(filters, 'ignoreSavedFilter') === 'true'){

          } else{
            this.cacheService.remove('searchFiltersAll');
            this.selectedFilters = _.cloneDeep(filters);
          }
        }
        this.emitFilterChangeEvent(true);
        this.hardRefreshFilter();
      }, error => {
        console.error('fetching filter data failed', error);
      });
  }

  private handleFilterChange() {
    this.filterChangeEvent.pipe(
      filter(({type, event}) => {
        if (type === 'mediaType' && this.selectedMediaTypeIndex !== event.data.index) {
          this.selectedMediaTypeIndex = event.data.index;
        }
        return true;
      }),
      debounceTime(1000)).subscribe(({ type, event }) => {
      this.emitFilterChangeEvent();
    });
  }

  public updateRoute() {
    let queryFilters = _.get(this.activatedRoute, 'snapshot.queryParams');
    if (this?.selectedFilters?.channel) {
      const channelIds = [];
      const facetsData = _.find(this.facets, {'name': 'channel'});
      _.forEach(this.selectedFilters.channel, (value, index) => {
        const data = _.find(facetsData.values, {'name': value});
        channelIds.push(data.identifier);
      });
      this.selectedFilters.channel = channelIds;
    }
    if(this?.utilService?.isDesktopApp && queryFilters?.selectedTab === 'mydownloads' && this.isConnected) {
      this.queryParamsToOmit = this.queryParamsToOmit && this.queryParamsToOmit.length ? this.queryParamsToOmit.push('key') : ['key']
      if(this.selectedFilters.key) {
        delete this.selectedFilters.key;
      }
    }
    if (this.queryParamsToOmit) {
      queryFilters = _.omit(_.get(this.activatedRoute, 'snapshot.queryParams'), this.queryParamsToOmit);
      queryFilters = {...queryFilters, ...this.selectedFilters};
    }
    if (this.cacheService.get('searchFiltersAll')) {
      this.selectedFilters['selectedTab'] = 'all';
    }
    this.router.navigate([], {
      queryParams: this.queryParamsToOmit ? queryFilters : { ...queryFilters, ...this.selectedFilters },
      relativeTo: this.activatedRoute.parent
    });
  }

  private emitFilterChangeEvent(skipUrlUpdate = false) {
    this.updateTerms();
    this.filterChange.emit({ status: 'FETCHED', filters: this.selectedFilters });
    if (!skipUrlUpdate) {
      this.updateRoute();
    } else if (this.cacheService.get('searchFiltersAll') && !_.get(this.selectedFilters, 'key')) {
      this.updateRoute();
    } else if (_.get(this.selectedFilters, 'key')) {
      this.updateRoute();
    }
  }

  private hardRefreshFilter() {
    this.refresh = false;
    this.cdr.detectChanges();
    this.refresh = true;
  }

  private setResetFilterInteractData() {
    setTimeout(() => { // wait for model to change
      this.resetFilterInteractEdata = {
        id: 'reset-filter',
        type: 'click',
        pageid: _.get(this.activatedRoute, 'snapshot.data.telemetry.pageid'),
        extra: { filters: this.selectedFilters }
      };
    }, 5);
  }

  removeFilterSelection(data) {
    _.map(this.selectedFilters, (value, key) => {
      if (this.selectedFilters[data.type] && !_.isEmpty(this.selectedFilters[data.type])) {
        _.remove(value, (n) => {
          return n === data.value && data.type === key;
        });
      }
      if (_.isEmpty(value)) { delete this.selectedFilters[key]; }
    });
    this.filterChange.emit({ status: 'FETCHED', filters: this.selectedFilters });
    this.updateRoute();
  }

  onSearchFacetFilterReset() {
    this.cacheService.remove('searchFiltersAll');
    /* istanbul ignore else */
    if (this.searchFacetFilterComponent) {
      this.searchFacetFilterComponent.resetFilter();
    }
    /* istanbul ignore next */
    this.router.navigate([], {
      queryParams: {
        ...(() => {
          const queryParams = _.cloneDeep(this.activatedRoute.snapshot.queryParams);
          const queryFilters = [...this.supportedFilterAttributes, ...['board', 'medium', 'gradeLevel', 'channel']];
          queryFilters.forEach((attr) => delete queryParams[attr]);
          return queryParams;
        })()
      },
      relativeTo: this.activatedRoute.parent
    });
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }

  updateTerms() {
    this.resourceService.languageSelected$.pipe(takeUntil(this.unsubscribe$)).subscribe((languageData) => {
      if (this.facets) {
        this.facets.forEach((facet) => {
          facet['label'] = this.utilService.transposeTerms(facet['label'], facet['label'], this.resourceService.selectedLang);
        });
      }
    });
  }
}
<ng-container *ngIf="facets && layoutConfiguration">
  <div class="sbt-filter">
    <mat-accordion class="sb-mat-accordion sbt-filter-accordion">
      <mat-expansion-panel [expanded]="isOpen">
        <mat-expansion-panel-header>
          <div class="sbt-filter-overlay"></div>
          <div class="sbt-filter-switcher-container cursor-pointer mobile only" (click)="isOpen = !isOpen" tabindex="0">
            <div class="sbt-filter-switcher"><i class="sliders horizontal icon"></i></div>
            <div class="sbt-filter-text">{{resourceService.frmelmnts?.lbl?.filters | translate}}</div>
          </div>
          <div class="sbt-filter-switcher-container cursor-pointer computer only">
            <div class="sbt-filter-switcher"><i class="sliders horizontal icon"></i></div>
            <div class="sbt-filter-text">{{resourceService.frmelmnts?.lbl?.filters | translate}}</div>
          </div>
        </mat-expansion-panel-header>
        <ng-container>
          <div class="sbt-filter-bar mr-16 pl-24">
            <div class="sbt-reset-bar d-flex flex-ai-center flex-ai-jc-center">
              <button class="sb-btn sb-btn-xs sb-btn-link-primary pull-right sbt-btn-reset cursor-pointer"
                (click)="onSearchFacetFilterReset()" tabindex="0" appTelemetryInteract
                [telemetryInteractEdata]="resetFilterInteractEdata">{{resourceService.frmelmnts?.btn?.reset |
                translate}} <i class="icon undo"></i></button>
              <span class="sbt-filter-close"><i class="icon-svg icon-svg--xxs icon-close cursor-pointer"
                  (click)="isOpen = !isOpen" tabindex="0" attr.aria-label="{{resourceService.frmelmnts?.btn?.close}}">
                  <svg class="icon icon-svg--red">
                    <use xlink:href="./assets/images/sprite.svg#close"></use>
                  </svg>
                </i>
              </span>
            </div>
            <div class="sbt-filter-scrollable pr-24">
              <div class="fsmall my-8 text-left filter-pref-text" tabindex="0">
                {{resourceService.frmelmnts?.lbl?.basedOnPref | translate}}</div>
              <sb-search-facet-filter *ngIf="filterFormTemplateConfig"
                [supportedFilterAttributes]="supportedFilterAttributes" [searchResultFacets]="facets"
                [filterFormTemplateConfig]="filterFormTemplateConfig"
                (searchFilterChange)="this.selectedFilters = $event; emitFilterChangeEvent(true)"
                #sbSearchFacetFilterComponent>
              </sb-search-facet-filter>
            </div>
          </div>
        </ng-container>
      </mat-expansion-panel>
    </mat-accordion>
  </div>
</ng-container>
<ng-container *ngIf="facets && !layoutConfiguration">
  <div class="sb-prominent-filter p-0 mt-8">
    <div class="ui container">
      <mat-accordion>
        <mat-expansion-panel>
          <mat-expansion-panel-header appTelemetryInteract [telemetryInteractEdata]="filterInteractEdata"
            [telemetryInteractCdata]="telemetryCdata">
            <mat-panel-title>
              {{resourceService.frmelmnts?.lbl?.filters}}
            </mat-panel-title>
          </mat-expansion-panel-header>
          <ng-container>
            <div class="mb-16" *ngIf="facets">
              <ng-container *ngFor="let facet of facets | sortBy:'index':'asc'">
                <div class="d-inline-block">
                  <div class="d-flex flex-ai-center pr-8">
                    <div *ngIf="selectedFilters[facet.name] && selectedFilters[facet.name].length > 0">
                      <label>{{facet.label}} :&nbsp;</label>
                      <ng-container *ngFor="let item of selectedFilters[facet.name]">
                        <a class="ui label mt-8 mr-8 text-capitalize" *ngIf="item && item.length > 0">
                          {{item}}
                          <i class="delete icon" tabindex="0"
                            (click)="removeFilterSelection({type: facet.name, value: item})"></i>
                        </a>
                      </ng-container>
                    </div>
                  </div>
                </div>
              </ng-container>
            </div>
            <div class="sb-prominent-filter-container sb-global-search" *ngIf="refresh">
              <div class="sb-prominent-filter-field" *ngFor="let facet of facets | sortBy:'index':'asc'">
                <!-- <label class="fnormal font-weight-bold d-flex">{{facet.label}} </label> -->
                <label *ngIf="facet.name !== 'mediaType'">
                  <sui-multi-select *ngIf="facet.name !== 'mediaType'" [hasLabels]="false"
                    defaultSelectionText={{facet.label}} zeroSelectionText={{resourceService.frmelmnts.lbl.Select}}
                    labelField="name" valueField="name" class="ui selection dropdown sbt-dropdown-tick multiple"
                    [(ngModel)]="selectedFilters[facet.name]" (ngModelChange)="onChange(facet)">
                    <div *ngIf="facet.name !== 'gradeLevel' else gradeLevel">
                      <sui-select-option class="text-capitalize"
                        *ngFor="let dropdownValue of facet.values | sortBy:'name':'asc'" [value]="dropdownValue">
                      </sui-select-option>
                    </div>
                    <ng-template #gradeLevel>
                      <sui-select-option class="text-capitalize" *ngFor="let dropdownValue of facet.values"
                        [value]="dropdownValue">
                      </sui-select-option>
                    </ng-template>
                  </sui-multi-select>
                </label>
                <!-- <div *ngIf="facet.name === 'mediaType'" class="sb-class-bar sb-bg-color-gray-0">
                  <sb-library-filters [list]="facet.mimeTypeList" [layout]="filterLayout.ROUND"
                    [selectedItems]="[facet.mimeTypeList.indexOf(selectedMediaType) !== -1 ? facet.mimeTypeList.indexOf(selectedMediaType): selectedMediaTypeIndex]"
                    (selectedFilter)="filterChangeEvent.next({ event: $event, type: 'mediaType'}); selectedFilters[facet.name]=[$event.data.text]">
                  </sb-library-filters>
                </div> -->
              </div>
              <div class="d-flex w-100" *ngFor="let facet of facets | sortBy:'index':'asc'">
                <div *ngIf="facet.name === 'mediaType'" class="sb-class-bar sb-bg-color-gray-0">
                  <label>
                    <sb-library-filters [list]="facet.mimeTypeList" [layout]="filterLayout.ROUND"
                      [selectedItems]="[facet.mimeTypeList.indexOf(selectedMediaType) !== -1 ? facet.mimeTypeList.indexOf(selectedMediaType): selectedMediaTypeIndex]"
                      (selectedFilter)="filterChangeEvent.next({ event: $event, type: 'mediaType'}); selectedFilters[facet.name]=[$event.data.text]">
                    </sb-library-filters>
                  </label>
                </div>
              </div>
              <div class="sb-prominent-filter-field">
                <button class="sb-btn sb-btn-normal sb-btn-outline-primary" (click)="resetFilters()" tabindex="0"
                  appTelemetryInteract [telemetryInteractEdata]="resetFilterInteractEdata">
                  {{resourceService.frmelmnts?.btn?.reset}}
                </button>
              </div>
            </div>
          </ng-container>
        </mat-expansion-panel>
      </mat-accordion>
    </div>
  </div>
</ng-container>

./global-search-filter.component.scss

@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;
@use "pages/sbt-filter" as *;
@use "components/sb-forms/facet-filters" as *;

:host ::ng-deep {
  .ui.selection.sbt-dropdown.dropdown .menu>div .item {
    color: var(--sbt-theme-purple-selectbox);
    padding: 0.75rem 1.25rem !important;
    font-size: 0.86rem;
    position: relative;
    cursor: pointer;
    display: block;
    border: none;
    height: auto;
    text-align: left;
    border-top: none;
    line-height: 1em;
    text-transform: none;
    box-shadow: none;
    -webkit-touch-callout: none;
  }
  .ui.dropdown .menu div .active.item{
      font-weight: 700 !important;
  }
  .ui.dropdown .menu>div .item:hover {
    background: rgba(var(--rc-rgba-black), 0.05);
  }
  .ui.dropdown .menu div .selected.item, .ui.dropdown.selected{
    font-weight: 700 !important;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""