Adding filter bar
[aai/sparky-fe.git] / src / app / vnfSearch / VnfSearch.jsx
index 5a35be3..d04bbd4 100644 (file)
 /*
- * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
  * Copyright © 2017 Amdocs
- * ================================================================================
+ * All rights reserved.
+ * ============================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *       http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=====================================================
  *
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
  */
 import React, {Component} from 'react';
 import {connect} from 'react-redux';
 import {
+  isEqual,
+  isEmpty
+} from 'lodash';
+import {VerticalFilterBar} from 'vertical-filter-bar';
+import {CollapsibleSlidingPanel} from 'collapsible-sliding-panel';
+
+import {setSecondaryTitle} from 'app/MainScreenWrapperActionHelper.js';
+import {
+  vnfActionTypes,
   VNF_TITLE,
   VNFS_ROUTE,
-  DEFAULT_VNFS_SEARCH_HASH
+  VNF_SEARCH_FILTER_NAME
 } from 'app/vnfSearch/VnfSearchConstants.js';
 import {
-  processTotalVnfVisualizationOnSearchChange,
-  processOrchStatusVisualizationOnSearchChange,
-  processProvStatusVisualizationOnSearchChange,
-  setNotificationText
+  processVnfVisualizationsOnFilterChange,
+  processVnfFilterPanelCollapse,
+  setNotificationText,
+  clearVnfSearchData
 } from 'app/vnfSearch/VnfSearchActions.js';
 import VnfSearchOrchStatusVisualizations from 'app/vnfSearch/VnfSearchOrchestratedStatusVisualization.jsx';
 import VnfSearchProvStatusVisualizations from 'app/vnfSearch/VnfSearchProvStatusVisualization.jsx';
+import VnfSearchNfTypeVisualizations from 'app/vnfSearch/VnfSearchNfTypeVisualization.jsx';
+import VnfSearchNfRoleVisualizations from 'app/vnfSearch/VnfSearchNfRoleVisualization.jsx';
 import VnfSearchTotalCountVisualization from 'app/vnfSearch/VnfSearchTotalCountVisualization.jsx';
 import i18n from 'utils/i18n/i18n';
-import {changeUrlAddress, buildRouteObj} from 'utils/Routes.js';
+import {changeUrlAddress, buildRouteObjWithFilters} from 'utils/Routes.js';
+import {
+  getUnifiedFilters,
+  processFilterSelection,
+  setNonConvertedFilterValues,
+  convertNonConvertedValues,
+  buildFilterValueMap,
+  setFilterSelectionsToDefaults,
+  FILTER_BAR_TITLE
+} from 'generic-components/filterBar/FilterBarUtils.js';
 
 const mapStateToProps = ({vnfSearch}) => {
   let {
         feedbackMsgText = '',
-        feedbackMsgSeverity = ''
+        feedbackMsgSeverity = '',
+        vnfFilters = {},
+        selectedFilterValues = {},
+        vnfFilterValues = {},
+        vnfVisualizationPanelClass = 'collapsible-panel-main-panel',
+        unifiedFilterValues = {},
+        nonConvertedFilters = {}
       } = vnfSearch;
 
   return {
     feedbackMsgText,
-    feedbackMsgSeverity
+    feedbackMsgSeverity,
+    vnfFilters,
+    selectedFilterValues,
+    vnfFilterValues,
+    vnfVisualizationPanelClass,
+    unifiedFilterValues,
+    nonConvertedFilters
   };
 };
 
 let mapActionToProps = (dispatch) => {
   return {
-    onReceiveNewParams: (vnfParam) => {
-      dispatch(processTotalVnfVisualizationOnSearchChange(vnfParam));
-      dispatch(processOrchStatusVisualizationOnSearchChange(vnfParam));
-      dispatch(processProvStatusVisualizationOnSearchChange(vnfParam));
+    onSetViewTitle: (title) => {
+      dispatch(setSecondaryTitle(title));
+    },
+    onInitializeVnfSearchFilters: () => {
+      // first time to the page, need to get the list of available filters
+      dispatch(getUnifiedFilters(VNF_SEARCH_FILTER_NAME, vnfActionTypes.VNF_SEARCH_FILTERS_RECEIVED));
+    },
+    onFilterPanelCollapse: (isOpen) => {
+      // expand/collapse the filter panel
+      dispatch(processVnfFilterPanelCollapse(isOpen));
+    },
+    onFilterSelection: (selectedFilters, allFilters) => {
+      // callback for filter bar whenever a selection is made... need to
+      // convert and save the selected value(s)
+      if (Object.keys(allFilters).length > 0) {
+        // only process the selection if allFilters has values (possible that
+        // filter bar is sending back the default filter selections before
+        // we have received the list of available filters i.e. allFilters)
+        dispatch(processFilterSelection(selectedFilters, allFilters));
+      }
+    },
+    onFilterValueChange: (convertedFilterValues) => {
+      // filter values have been converted, now update the VNF visualizations
+      dispatch(processVnfVisualizationsOnFilterChange(convertedFilterValues));
+    },
+    onReceiveNewFilterValueParams: (filterValueString) => {
+      // new filter values have been received as URL parameters, save the
+      // non-converted values (later to be converted and sent to filter bar)
+      // and update the VNF visualizations
+      let filterValueMap =  buildFilterValueMap(filterValueString);
+
+      dispatch(setNonConvertedFilterValues(filterValueMap));
+      dispatch(processVnfVisualizationsOnFilterChange(filterValueMap));
+
+      // incase url param was changed manually, need to update vnfFilterValues
+    },
+    onResetFilterBarToDefaults: (filters, filterValues) => {
+      dispatch(setFilterSelectionsToDefaults(filters, filterValues));
+    },
+    onPrepareToUnmount: () => {
+      // clean things up:
+      // 1- clear the VNF data
+      // 2- ensure filter bar is closed
+      dispatch(clearVnfSearchData());
+      dispatch(processVnfFilterPanelCollapse(false));
+    },
+    onConvertFilterValues: (nonConvertedValues, allFilters, currentlySetFilterValues) => {
+      // we have saved non-converted filter values received from URL params,
+      // time to convert them so can update filter bar selections programatically
+      dispatch(convertNonConvertedValues(nonConvertedValues, allFilters, currentlySetFilterValues));
     },
     onMessageStateChange: (msgText, msgSeverity) => {
       dispatch(setNotificationText(msgText, msgSeverity));
@@ -65,16 +145,25 @@ let mapActionToProps = (dispatch) => {
 };
 
 class vnfSearch extends Component {
+  static propTypes = {
+    feedbackMsgText: React.PropTypes.string,
+    feedbackSeverity: React.PropTypes.string,
+    vnfFilters: React.PropTypes.object,
+    selectedFilterValues: React.PropTypes.object,
+    vnfFilterValues: React.PropTypes.object,
+    vnfVisualizationPanelClass: React.PropTypes.string,
+    unifiedFilterValues: React.PropTypes.object,
+    nonConvertedFilters: React.PropTypes.object
+  };
+
   componentWillMount() {
+    this.props.onSetViewTitle(i18n(VNF_TITLE));
+    this.props.onInitializeVnfSearchFilters();
+
     if (this.props.match &&
       this.props.match.params &&
-      this.props.match.params.vnfParam) {
-      this.props.onReceiveNewParams(this.props.match.params.vnfParam);
-    } else {
-      // render using default search params (hash for "VNFs")
-      this.props.onReceiveNewParams(DEFAULT_VNFS_SEARCH_HASH);
-      changeUrlAddress(buildRouteObj(VNFS_ROUTE, DEFAULT_VNFS_SEARCH_HASH),
-        this.props.history);
+      this.props.match.params.filters) {
+      this.props.onReceiveNewFilterValueParams(this.props.match.params.filters);
     }
 
     if (this.props.feedbackMsgText) {
@@ -84,42 +173,90 @@ class vnfSearch extends Component {
   }
 
   componentWillReceiveProps(nextProps) {
-    if (nextProps.match.params.vnfParam) {
-      if (nextProps.match.params.vnfParam !==
-        this.props.match.params.vnfParam) {
-        this.props.onReceiveNewParams(nextProps.match.params.vnfParam);
-      }
-    } else if (this.props.match.params.vnfParam) {
-      // currently on VNF page and somebody has clicked the VNF NavLink
-      // want to reload the view with the default params (hash for "NFVs")
-      this.props.onReceiveNewParams(DEFAULT_VNFS_SEARCH_HASH);
-      changeUrlAddress(buildRouteObj(VNFS_ROUTE, DEFAULT_VNFS_SEARCH_HASH),
-        this.props.history);
-    }
-
     if (nextProps.feedbackMsgText &&
       nextProps.feedbackMsgText !==
       this.props.feedbackMsgText) {
       this.props.onMessageStateChange(nextProps.feedbackMsgText,
         nextProps.feedbackMsgSeverity);
     }
+
+    if (nextProps.vnfFilterValues &&
+      !isEqual(nextProps.vnfFilterValues, this.props.vnfFilterValues) &&
+      this.props.vnfFilters) {
+      this.props.onFilterValueChange(nextProps.vnfFilterValues);
+      changeUrlAddress(buildRouteObjWithFilters(VNFS_ROUTE, nextProps.vnfFilterValues),
+        this.props.history);
+    }
+
+    if (nextProps.match &&
+      nextProps.match.params &&
+      nextProps.match.params.filters &&
+      !isEqual(nextProps.match.params.filters, this.props.match.params.filters)) {
+      // added line below to reload the filters if filter changes, this will load new filters
+      this.props.onInitializeVnfSearchFilters();
+      this.props.onReceiveNewFilterValueParams(nextProps.match.params.filters);
+    } else if (Object.keys(nextProps.nonConvertedFilters).length > 0 &&
+      !isEqual(this.props.nonConvertedFilters, nextProps.nonConvertedFilters)) {
+      if (Object.keys(this.props.vnfFilters).length > 0) {
+        this.props.onConvertFilterValues(
+          nextProps.nonConvertedFilters, this.props.vnfFilters, this.props.vnfFilterValues);
+      }
+    } else if ((!nextProps.match || !nextProps.match.params || !nextProps.match.params.filters) &&
+      this.props.match.params.filters && this.props.vnfFilters && this.props.vnfFilterValues) {
+      // VNF Search navigation button was pressed while the view is still visible ... need to reset
+      // the filter bar selections to the default values
+      this.props.onResetFilterBarToDefaults(this.props.vnfFilters, this.props.vnfFilterValues);
+    }
+
+    if (nextProps.vnfFilters && !isEqual(nextProps.vnfFilters, this.props.vnfFilters) &&
+      Object.keys(this.props.nonConvertedFilters).length > 0) {
+      // just received list of available filters and there is are nonConvertedFilters (previously
+      // set from url params), need to convert those values and update the filter bar selections
+      this.props.onConvertFilterValues(
+        this.props.nonConvertedFilters, nextProps.vnfFilters, this.props.vnfFilterValues);
+    } else if (nextProps.vnfFilters && !isEqual(nextProps.vnfFilters, this.props.vnfFilters) &&
+      isEmpty(this.props.vnfFilterValues)) {
+      // filter bar previously returned the default filter selections (but we didn't have the list
+      // of available filters at the time, so couldn't do anything. Now receiving the list of
+      // available filters, so triger the filter selection action in order to load the visualization data
+      this.props.onResetFilterBarToDefaults(nextProps.vnfFilters, this.props.vnfFilterValues);
+    }
   }
 
   componentWillUnmount() {
-    // resetting to default params so on relaunch there will be no
-    // visibility of old searches
-    this.props.onReceiveNewParams(DEFAULT_VNFS_SEARCH_HASH);
+    // set the data to 'NO DATA' so upon return, the view is rendered with
+    // no data until the request for new data is returned
+    this.props.onPrepareToUnmount();
+  }
+
+  getFilterBar() {
+    return (
+      <VerticalFilterBar
+        filtersConfig={this.props.vnfFilters}
+        filterValues={this.props.unifiedFilterValues}
+        filterTitle={FILTER_BAR_TITLE}
+        onFilterChange={(selectedFilters) =>
+          this.props.onFilterSelection(selectedFilters, this.props.vnfFilters)} />    );
   }
 
   render() {
+    let filterBar = this.getFilterBar();
+
     return (
-      <div>
-        <div className='secondary-header'>
-          <span className='secondary-title'>{i18n(VNF_TITLE)}</span>
-        </div>
-        <VnfSearchTotalCountVisualization />
-        <VnfSearchProvStatusVisualizations />
-        <VnfSearchOrchStatusVisualizations />
+      <div className='view-container'>
+        <CollapsibleSlidingPanel
+          slidingPanelClassName='collapsible-sliding-panel'
+          slidingPanelClosedClassName='collapsible-sliding-panel-is-closed'
+          expanderHandleClassName='collapsible-sliding-panel-expander'
+          slidingPanelContent={filterBar}>
+          <div className={this.props.vnfVisualizationPanelClass}>
+            <VnfSearchTotalCountVisualization />
+            <VnfSearchProvStatusVisualizations />
+            <VnfSearchOrchStatusVisualizations />
+            <VnfSearchNfTypeVisualizations />
+            <VnfSearchNfRoleVisualizations />
+          </div>
+        </CollapsibleSlidingPanel>
       </div>
     );
   }