You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by ak...@apache.org on 2020/05/12 17:03:15 UTC

[incubator-pinot] branch master updated: [TE] frontend - harleyjj/deprecated - remove dead routes from frontend (#5315)

This is an automated email from the ASF dual-hosted git repository.

akshayrai09 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new e88f6c2  [TE] frontend - harleyjj/deprecated - remove dead routes from frontend (#5315)
e88f6c2 is described below

commit e88f6c29d13c8f78f94815ad819c086500b5bce8
Author: Harley Jackson <hj...@linkedin.com>
AuthorDate: Tue May 12 10:03:06 2020 -0700

    [TE] frontend - harleyjj/deprecated - remove dead routes from frontend (#5315)
---
 .../app/pods/auto-onboard/controller.js            | 280 -------
 .../app/pods/auto-onboard/route.js                 |  24 -
 .../app/pods/auto-onboard/template.hbs             | 140 ----
 .../app/pods/manage/alert/controller.js            |  52 --
 .../app/pods/manage/alert/edit/controller.js       | 215 ------
 .../app/pods/manage/alert/edit/route.js            | 164 -----
 .../app/pods/manage/alert/edit/template.hbs        |  86 ---
 .../app/pods/manage/alert/explore/controller.js    | 816 ---------------------
 .../app/pods/manage/alert/explore/route.js         | 605 ---------------
 .../app/pods/manage/alert/explore/template.hbs     | 364 ---------
 .../app/pods/manage/alert/route.js                 | 171 -----
 .../app/pods/manage/alert/template.hbs             |  59 --
 .../app/pods/manage/alert/tune/controller.js       | 446 -----------
 .../app/pods/manage/alert/tune/route.js            | 487 ------------
 .../app/pods/manage/alert/tune/template.hbs        | 257 -------
 .../app/pods/preview/controller.js                 | 478 ------------
 .../thirdeye-frontend/app/pods/preview/route.js    |  24 -
 .../app/pods/preview/template.hbs                  |  82 ---
 thirdeye/thirdeye-frontend/app/router.js           |   7 -
 19 files changed, 4757 deletions(-)

diff --git a/thirdeye/thirdeye-frontend/app/pods/auto-onboard/controller.js b/thirdeye/thirdeye-frontend/app/pods/auto-onboard/controller.js
deleted file mode 100644
index 4f0657f..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/auto-onboard/controller.js
+++ /dev/null
@@ -1,280 +0,0 @@
-import {observer, computed, set, get} from '@ember/object';
-import Controller from '@ember/controller';
-import {checkStatus} from 'thirdeye-frontend/utils/utils';
-import fetch from 'fetch';
-
-export default Controller.extend({
-  detectionConfigId: null,
-
-  detectionConfigName: null,
-
-  datasetName: null,
-
-  datasets: null,
-
-  dimensions: null,
-
-  selectedMetric: null,
-
-  filterOptions: null,
-
-  metrics: null,
-
-  metricsProperties: null,
-
-  metricUrn: null,
-
-  filterMap: null,
-
-  toggleChecked: false,
-
-  output: null,
-
-  topk: null,
-
-  minContribution: null,
-
-  minValue: null,
-
-  idToNames: null,
-
-  selectedDimensions: '{}',
-
-  selectedFilters: '{}',
-
-  queryParams: ['detectionId'],
-
-  generalFieldsEnabled: computed.or('dimensions'),
-
-  metricsFieldEnabled: computed.or('metrics'),
-
-  // TODO: replace with ember data
-  hasDetectionId: observer('detectionId', async function () {
-    const detectionId = this.get('detectionId');
-    const res = await fetch(`/dataset-auto-onboard/` + detectionId).then(checkStatus);
-    const nestedProperties = res['properties']['nested'];
-    const nestedProperty = nestedProperties[0];
-
-    // fill in values:
-    this.setProperties({
-      detectionConfigId: res['id'],
-      detectionConfigName: res['name'],
-      topk: nestedProperty['k'],
-      minValue: nestedProperty['minValue'],
-      minContribution: nestedProperty['minContribution'],
-      datasetName: res['properties']['datasetName']
-    });
-
-    await this._datasetNameChanged();
-
-    let dimensionBreakdownUrn = null;
-    const idToNames = this.get('idToNames');
-    const metricsProperties = get(this, 'metricsProperties');
-
-    // fill in dimensions
-    this.set('selectedDimensions', JSON.stringify({
-      'dimensions': nestedProperty['dimensions']
-    }));
-
-    // fill in filters
-    let urnPieces = nestedProperty['metricUrn'].split(':');
-    const filters = {};
-    let i;
-    for (i = 3; i < urnPieces.length; i++) {
-      const filter = urnPieces[i].split('=');
-      if (filter[0] in filters) {
-        filters[filter[0]].push(filter[1]);
-      } else {
-        filters[filter[0]] = [filter[1]];
-      }
-    }
-    this.set('selectedFilters', JSON.stringify(filters));
-    this._updateFilters();
-
-    // fill in selected metrics
-    const metricIds = nestedProperties.reduce(function (obj, property) {
-      let urn;
-      if ('nestedMetricUrn' in property) {
-        urn = property['nestedMetricUrn'];
-        dimensionBreakdownUrn = property['metricUrn'];
-      } else {
-        urn = property['metricUrn'];
-      }
-      obj.push(urn.split(':')[2]);
-      return obj;
-    }, []);
-
-    Object.keys(metricsProperties).forEach(function (key) {
-      if (metricIds.indexOf(metricsProperties[key]['id'].toString()) == -1) {
-        set(metricsProperties[key], 'monitor', false);
-      }
-    });
-
-    // fill in dimension breakdown metric
-    this.set('selectedMetric', idToNames[this._metricUrnToId(dimensionBreakdownUrn)]);
-  }),
-
-  // TODO: replace with ember data
-  _writeDetectionConfig(detectionConfigBean) {
-    const jsonString = JSON.stringify(detectionConfigBean);
-    return fetch(`/thirdeye/entity?entityType=DETECTION_CONFIG`, {method: 'POST', body: jsonString})
-      .then(checkStatus)
-      .then(res => set(this, 'output', `saved '${detectionConfigBean.name}' as id ${res}`))
-      .catch(err => set(this, 'output', err));
-  },
-
-  _metricUrnToId(metricUrn) {
-    return metricUrn.split(':')[2];
-  },
-
-  _updateFilters() {
-    const filters = this.get('selectedFilters');
-    const metricsProperties = get(this, 'metricsProperties');
-    const filterMap = JSON.parse(filters);
-    Object.keys(metricsProperties).forEach(function (key) {
-      const metricProperty = metricsProperties[key];
-      let metricUrn = "thirdeye:metric:" + metricProperty['id'];
-      Object.keys(filterMap).forEach(function (key) {
-        filterMap[key].forEach(function (value) {
-          metricUrn = metricUrn + ":" + key + "=" + value;
-        });
-      });
-      metricsProperties[key]['urn'] = metricUrn;
-    });
-  },
-
-  // TODO: replace with ember data
-  async _datasetNameChanged() {
-    const url = `/dataset-auto-onboard/metrics?dataset=` + get(this, 'datasetName');
-    const res = await fetch(url).then(checkStatus);
-    const metricsProperties = res.reduce(function (obj, metric) {
-      obj[metric["name"]] = {
-        "id": metric['id'], "urn": "thirdeye:metric:" + metric['id'], "monitor": true
-      };
-      return obj;
-    }, {});
-    const metricUrn = metricsProperties[Object.keys(metricsProperties)[0]]['id'];
-    const idToNames = {};
-    Object.keys(metricsProperties).forEach(function (key) {
-      idToNames[metricsProperties[key]['id']] = key;
-    });
-
-    this.setProperties({
-      metricsProperties: metricsProperties,
-      metrics: Object.keys(metricsProperties),
-      idToNames: idToNames,
-      metricUrn: metricUrn
-    });
-
-    const result = await fetch(`/data/autocomplete/filters/metric/${metricUrn}`).then(checkStatus);
-
-    this.setProperties({
-      filterOptions: result, dimensions: {dimensions: Object.keys(result)}
-    });
-  },
-
-  actions: {
-    onSave(dataset) {
-      this.set('datasetName', dataset);
-      this._datasetNameChanged();
-    },
-
-    toggleCheckBox(name) {
-      const metricsProperties = get(this, 'metricsProperties');
-      set(metricsProperties[name], 'monitor', !metricsProperties[name]['monitor']);
-    },
-
-    onChangeValue(property, value) {
-      this.set(property, value);
-    },
-
-    onFilters(filters) {
-      this.set('selectedFilters', filters);
-      this._updateFilters();
-    },
-
-    onSubmit() {
-      const metricsProperties = get(this, 'metricsProperties');
-      const nestedProperties = [];
-      const selectedMetric = this.get('selectedMetric');
-      const detectionConfigId = this.get('detectionConfigId');
-      const selectedDimensions = JSON.parse(this.get('selectedDimensions'));
-      const topk = this.get('topk');
-      const minValue = this.get('minValue');
-      const minContribution = this.get('minContribution');
-      Object.keys(metricsProperties).forEach(function (key) {
-        const properties = metricsProperties[key];
-        if (!properties['monitor']) {
-          return;
-        }
-        const detectionConfig = {
-          className: 'org.apache.pinot.thirdeye.detection.algorithm.DimensionWrapper', nested: [{
-            className: 'org.apache.pinot.thirdeye.detection.algorithm.MovingWindowAlgorithm',
-            baselineWeeks: 4,
-            windowSize: '4 weeks',
-            changeDuration: '7d',
-            outlierDuration: '12h',
-            aucMin: -10,
-            zscoreMin: -4,
-            zscoreMax: 4
-          }]
-        };
-        if (selectedMetric == null) {
-          detectionConfig['metricUrn'] = properties['urn'];
-        } else {
-          detectionConfig['metricUrn'] = metricsProperties[selectedMetric]['urn'];
-          detectionConfig['nestedMetricUrn'] = properties['urn'];
-        }
-        if (selectedDimensions != null) {
-          detectionConfig['dimensions'] = selectedDimensions['dimensions'];
-        }
-        if (topk != null) {
-          detectionConfig['k'] = parseInt(topk);
-        }
-        if (minValue != null) {
-          detectionConfig['minValue'] = parseFloat(minValue);
-        }
-        if (minContribution != null) {
-          detectionConfig['minContribution'] = parseFloat(minContribution);
-        }
-        nestedProperties.push(detectionConfig);
-      });
-
-      const configResult = {
-        "cron": "45 10/15 * * * ? *", "name": get(this, 'detectionConfigName'), "lastTimestamp": 0, "properties": {
-          "className": "org.apache.pinot.thirdeye.detection.algorithm.MergeWrapper",
-          "maxGap": 7200000,
-          "nested": nestedProperties,
-          "datasetName": get(this, 'datasetName')
-        }
-      };
-
-      if (detectionConfigId != null) {
-        configResult['id'] = detectionConfigId;
-      }
-      this._writeDetectionConfig(configResult);
-    },
-
-    onSelectDimension(dims) {
-      this.set('selectedDimensions', dims);
-    },
-
-    onChangeName(name) {
-      this.set('detectionConfigName', name);
-    },
-
-    onSelectMetric(name) {
-      this.set('selectedMetric', name);
-    },
-
-    async onLoadDatasets() {
-      const url = `/thirdeye/entity/DATASET_CONFIG`;
-      const res = await fetch(url).then(checkStatus);
-      const datasets = res.reduce(function (obj, datasetConfig) {
-        obj.push(datasetConfig['dataset']);
-        return obj;
-      }, []);
-      this.set('datasets', datasets);
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/auto-onboard/route.js b/thirdeye/thirdeye-frontend/app/pods/auto-onboard/route.js
deleted file mode 100644
index 8cdaf28..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/auto-onboard/route.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import Route from '@ember/routing/route';
-import { inject as service } from '@ember/service';
-
-export default Route.extend({
-  session: service(),
-  actions: {
-    /**
-     * save session url for transition on login
-     * @method willTransition
-     */
-    willTransition(transition) {
-      //saving session url - TODO: add a util or service - lohuynh
-      if (transition.intent.name && transition.intent.name !== 'logout') {
-        this.set('session.store.fromUrl', {lastIntentTransition: transition});
-      }
-    },
-    error() {
-      // The `error` hook is also provided the failed
-      // `transition`, which can be stored and later
-      // `.retry()`d if desired.
-      return true;
-    },
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/auto-onboard/template.hbs b/thirdeye/thirdeye-frontend/app/pods/auto-onboard/template.hbs
deleted file mode 100644
index 5771497..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/auto-onboard/template.hbs
+++ /dev/null
@@ -1,140 +0,0 @@
-<div class="container">
-  <h3>Dataset auto onboard</h3>
-  {{#power-select
-    classNames="te-input"
-    options=datasets
-    selected=datasetName
-    onchange=(action "onSave")
-    onopen=(action "onLoadDatasets")
-    loadingMessage="Waiting for the server...."
-    placeholder="Choose a dataset to onboard"
-    searchPlaceholder="Type to filter..."
-    triggerId="select-dataset"
-    triggerClass="te-form__select"
-    disabled=false
-  as |dataset|
-  }}
-    {{dataset}}
-  {{/power-select}}
-  {{#if metricUrn}}
-    <div class="row">
-      <label class="control-label te-label te-label--taller">
-        Detection Name:
-      </label>
-    </div>
-    {{input
-      type="text"
-      id="anomaly-form-function-name"
-      class="form-control te-input te-input--read-only"
-      placeholder="Add a descriptive name"
-      value=detectionConfigName
-      key-up=(action "onChangeName" detectionConfigName)
-    }}
-    <div class="row">
-      <label class="control-label te-label te-label--taller">
-        Choose the metrics:
-      </label>
-    </div>
-    {{#each-in metricsProperties as |name metric|}}
-      <div>
-        {{name}}
-        <input type="checkbox"
-               checked={{metric.monitor}}
-                 onclick={{action "toggleCheckBox" name}}/>
-      </div>
-    {{/each-in}}
-    <div class="row">
-      <label class="control-label te-label te-label--taller">
-        Filter metric by(Optional):
-        <span>
-          <i class="glyphicon glyphicon-question-sign"></i>
-          {{#tooltip-on-element class="te-tooltip"}}
-            For example, filter on countryCode::US implies only anomalies in US will be detected.
-          {{/tooltip-on-element}}
-        </span>
-      </label>
-    </div>
-    {{filter-select
-      options=filterOptions
-      selected=selectedFilters
-      triggerId="select-filters"
-      onChange=(action "onFilters")
-    }}
-    <div class="row">
-      <label for="select-dimension" class="control-label te-label te-label--taller">
-        Create an alert for each dimension value in: (optional)
-        <span>
-          <i class="glyphicon glyphicon-question-sign"></i>
-          {{#tooltip-on-element class="te-tooltip"}}
-            For example, selecting Continent means anomalies on each continent will be monitored.
-          {{/tooltip-on-element}}
-        </span>
-      </label>
-      {{filter-select
-        selected=selectedDimensions
-        options=dimensions
-        triggerId="select-dimensions"
-        onChange=(action "onSelectDimension")
-      }}
-    </div>
-    <div class="row">
-      <label class="control-label te-label te-label--taller">
-        Set up the business rules(Optional):
-        <span>
-          <i class="glyphicon glyphicon-question-sign"></i>
-          {{#tooltip-on-element class="te-tooltip"}}
-            For example, selecting page_view metric and set top k dimensions to 5 means the dimensions that have top 5 page_view will be monitored.
-          {{/tooltip-on-element}}
-        </span>
-      </label>
-    </div>
-    <label for="select-dimension" class="control-label te-label te-label--taller">
-      Metric used in dimension breakdown
-    </label>
-    {{#power-select
-      classNames="te-input"
-      options=metrics
-      selected=selectedMetric
-      onchange=(action "onSelectMetric")
-      loadingMessage="Waiting for the server...."
-      placeholder="Choose metric for rules"
-      searchPlaceholder="Type to filter..."
-      triggerId="select-metric"
-      triggerClass="te-form__select"
-      disabled=(not metricsFieldEnabled)
-    as |metric|
-    }}
-      {{metric}}
-    {{/power-select}}
-    <div class="col-xs-4">
-      <label class="control-label te-label te-label--taller">
-        Explore top k dimensions:
-      </label>
-      <input type="text"
-             value={{topk}}
-               onChange={{action (action "onChangeValue" "topk") value="target.value"}}/>
-    </div>
-    <div class="col-xs-4">
-      <label class="control-label te-label te-label--taller">
-        Min value:
-      </label>
-      <input type="text"
-             value={{minValue}}
-               onChange={{action (action "onChangeValue" "minValue") value="target.value"}}/>
-    </div>
-    <div class="col-xs-4">
-      <label class="control-label te-label te-label--taller">
-        Min contribution:
-      </label>
-      <input type="text"
-             value={{minContribution}}
-               onChange={{action (action "onChangeValue" "minContribution") value="target.value"}}/>
-    </div>
-    <div class="row">
-      <button onClick={{action "onSubmit"}}>submit</button>
-    </div>
-  {{/if}}
-  <div class="row">
-    {{output}}
-  </div>
-</div>
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/controller.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/controller.js
deleted file mode 100644
index a5db82b..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/controller.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Controller for Alert Landing and Details Page
- * @module manage/alert
- * @exports manage/alert
- */
-import Controller from '@ember/controller';
-import { setProperties } from '@ember/object';
-
-export default Controller.extend({
-  /**
-   * Set up to receive prompt to trigger page mode change.
-   * When replay id is received it indicates that we need to check replay status
-   * before displaying alert function performance data.
-   */
-  queryParams: ['jobId', 'functionName'],
-  jobId: null,
-  functionName: null,
-  isOverviewLoaded: true,
-
-  actions: {
-
-    /**
-     * Placeholder for subscribe button click action
-     */
-    onClickAlertSubscribe() {
-      // TODO: Set user as watcher for this alert when API ready
-    },
-
-    /**
-     * Handle conditions for display of appropriate alert nav link (overview or edit)
-     */
-    setEditModeActive() {
-      this.set('isEditModeActive', true);
-    },
-
-    /**
-     * Handle navigation to edit route
-     */
-    onClickEdit() {
-      this.set('isEditModeActive', true);
-      this.transitionToRoute('manage.alert.edit', this.get('id'), { queryParams: { refresh: true }});
-    },
-
-    /**
-     * Navigate to Alert Page
-     */
-    onClickNavToOverview() {
-      this.set('isEditModeActive', false);
-      this.transitionToRoute('manage.alert.explore', this.get('id'));
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/controller.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/controller.js
deleted file mode 100644
index 4757b8b..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/controller.js
+++ /dev/null
@@ -1,215 +0,0 @@
-/**
- * Handles alert edit form
- * @module manage/alert/edit
- * @exports manage/alert/edit
- */
-import { reads, or } from '@ember/object/computed';
-import fetch from 'fetch';
-import Controller from '@ember/controller';
-import {
-  set,
-  get,
-  computed,
-  setProperties
-} from '@ember/object';
-import {
-  checkStatus,
-  postProps
-} from 'thirdeye-frontend/utils/utils';
-import {
-  selfServeApiCommon,
-  selfServeApiOnboard
-} from 'thirdeye-frontend/utils/api/self-serve';
-
-export default Controller.extend({
-
-  /**
-   * Optional query param to refresh model
-   */
-  queryParams: ['refresh'],
-  refresh: null,
-
-  /**
-   * Important initializations
-   */
-  isEditAlertSuccess: false, // alert save success
-  isProcessingForm: false, // to trigger submit disable
-  isExiting: false, // exit detection
-  showManageGroupsModal: false, // manage group modal
-
-  /**
-   * The config group that the current alert belongs to
-   * @type {Object}
-   */
-  originalConfigGroup: reads('model.originalConfigGroup'),
-
-  /**
-   * Mapping alertFilter's pattern to human readable strings
-   * @returns {String}
-   */
-  pattern: computed('alertProps', function() {
-    const props = this.get('alertProps');
-    const patternObj = props.find(prop => prop.name === 'pattern');
-    const pattern = patternObj ? decodeURIComponent(patternObj.value) : 'Up and Down';
-
-    return pattern;
-  }),
-
-  /**
-   * Extracting Weekly Effect from alert Filter
-   * @returns {String}
-   */
-  weeklyEffect: computed('alertFilters.weeklyEffectModeled', function() {
-    const weeklyEffect = this.getWithDefault('alertFilters.weeklyEffectModeled', true);
-
-    return weeklyEffect;
-  }),
-
-  /**
-   * Extracting sensitivity from alert Filter and maps it to human readable values
-   * @returns {String}
-   */
-  sensitivity: computed('alertProps', function() {
-    const props = this.get('alertProps');
-    const sensitivityObj = props.find(prop => prop.name === 'sensitivity');
-    const sensitivity = sensitivityObj ? decodeURIComponent(sensitivityObj.value) : 'MEDIUM';
-
-    const sensitivityMapping = {
-      LOW: 'Robust (Low)',
-      MEDIUM: 'Medium',
-      HIGH: 'Sensitive (High)'
-    };
-
-    return sensitivityMapping[sensitivity];
-  }),
-
-  /**
-   * Disable submit under these circumstances
-   * @method isSubmitDisabled
-   * @return {Boolean} show/hide submit
-   */
-  isSubmitDisabled: or('{isProcessingForm,isAlertNameDuplicate}'),
-
-  /**
-   * Fetches an alert function record by name.
-   * Use case: when user names an alert, make sure no duplicate already exists.
-   * @method _fetchAlertByName
-   * @param {String} functionName - name of alert or function
-   * @return {Promise}
-   */
-  _fetchAlertByName(functionName) {
-    const url = selfServeApiCommon.alertFunctionByName(functionName);
-    return fetch(url).then(checkStatus);
-  },
-
-  /**
-   * Display success banners while model reloads
-   * @method confirmEditSuccess
-   * @return {undefined}
-   */
-  confirmEditSuccess() {
-    this.set('isEditAlertSuccess', true);
-  },
-
-  /**
-   * Reset fields to model init state
-   * @method clearAll
-   * @return {undefined}
-   */
-  clearAll() {
-    this.setProperties({
-      model: null,
-      isExiting: true,
-      isSubmitDisabled: false,
-      isEmailError: false,
-      isDuplicateEmail: false,
-      isEditAlertSuccess: false,
-      isNewConfigGroupSaved: false,
-      isProcessingForm: false,
-      isActive: false,
-      isLoadError: false,
-      updatedRecipients: [],
-      granularity: null,
-      alertFilters: null,
-      alertFunctionName: null,
-      alertId: null,
-      loadErrorMessage: null
-    });
-  },
-
-  /**
-   * Actions for edit alert form view
-   */
-  actions: {
-
-    /**
-     * Make sure alert name does not already exist in the system
-     * Either add or clear the "is duplicate name" banner
-     * @method validateAlertName
-     * @param {String} userProvidedName - The new alert name
-     * @return {undefined}
-     */
-    validateAlertName(userProvidedName) {
-      this._fetchAlertByName(userProvidedName).then(matchingAlerts => {
-        const isDuplicateName = matchingAlerts.find(alert => alert.functionName === userProvidedName);
-        this.set('isAlertNameDuplicate', isDuplicateName);
-      });
-    },
-
-    /**
-     * Action handler for displaying groups modal
-     * @returns {undefined}
-     */
-    onShowManageGroupsModal() {
-      set(this, 'showManageGroupsModal', true);
-    },
-
-    /**
-     * Action handler for CANCEL button - simply reset all fields
-     * @returns {undefined}
-     */
-    onCancel() {
-      const alertId = get(this, 'alertId');
-      this.send('refreshModel');
-      this.transitionToRoute('manage.alert.explore', alertId);
-    },
-
-    /**
-     * Action handler for form submit
-     * MVP Version: Can activate/deactivate and update alert name and edit config group data
-     * @returns {Promise}
-     */
-    onSubmit() {
-      const {
-        isActive,
-        alertFunctionName,
-        alertData: postFunctionBody
-      } = this.getProperties(
-        'isActive',
-        'alertFunctionName',
-        'alertData'
-      );
-
-      // Disable submit for now and make sure we're clear of email errors
-      set(this, 'isProcessingForm', true);
-
-      // Assign these fresh editable values to the Alert object currently being edited
-      setProperties(postFunctionBody, {
-        isActive,
-        functionName: alertFunctionName
-      });
-
-      // Step 1: Save any edits to the Alert entity in our DB
-      return fetch(selfServeApiOnboard.editAlert, postProps(postFunctionBody))
-        .then(res => checkStatus(res, 'post'))
-        .then(() => {
-          this.send('confirmSaveStatus', true);
-          set(this, 'isProcessingForm', false);
-        })
-        .catch(() => {
-          this.send('confirmSaveStatus', false);
-          this.clearAll();
-        });
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/route.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/route.js
deleted file mode 100644
index c31d7d53..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/route.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
- * Handles the 'edit' route for manage alert
- * @module manage/alert/edit/edit
- * @exports manage/alert/edit/edit
- */
-import RSVP from 'rsvp';
-import fetch from 'fetch';
-import Route from '@ember/routing/route';
-import { task } from 'ember-concurrency';
-import { get } from '@ember/object';
-import { checkStatus } from 'thirdeye-frontend/utils/utils';
-import { selfServeApiCommon } from 'thirdeye-frontend/utils/api/self-serve';
-import { inject as service } from '@ember/service';
-
-export default Route.extend({
-  session: service(),
-  notifications: service('toast'),
-
-  /**
-   * Optional params to load a fresh view
-   */
-  queryParams: {
-    refresh: {
-      refreshModel: true,
-      replace: false
-    }
-  },
-
-  async model(params, transition) {
-    const {
-      id,
-      alertData
-    } = this.modelFor('manage.alert');
-
-    if (!id) { return; }
-
-    const alertGroups = await fetch(selfServeApiCommon.configGroupByAlertId(id)).then(checkStatus);
-
-    return RSVP.hash({
-      alertGroups,
-      alertData
-    });
-  },
-
-  afterModel(model) {
-    const {
-      alertData,
-      alertGroups
-    } = model;
-
-    const {
-      properties: alertProps
-    } = alertData;
-
-    // Add a parsed properties array to the model
-    const propsArray = alertProps.split(';').map((prop) => {
-      const [ name, value ] = prop.split('=');
-      return { name, value: decodeURIComponent(value) };
-    });
-
-    Object.assign(model, {
-      propsArray,
-      alertGroups
-    });
-  },
-
-  setupController(controller, model) {
-    this._super(controller, model);
-
-    const {
-      alertData,
-      alertGroups,
-      propsArray: alertProps,
-      loadError: isLoadError,
-      loadErrorMsg: loadErrorMessage
-    } = model;
-
-    const {
-      isActive,
-      bucketSize,
-      bucketUnit,
-      id: alertId,
-      filters: alertFilters,
-      functionName: alertFunctionName
-    } = alertData;
-
-    controller.setProperties({
-      model,
-      alertData,
-      alertFilters,
-      alertProps,
-      alertFunctionName,
-      alertId,
-      alertGroups,
-      isActive,
-      isLoadError,
-      loadErrorMessage,
-      granularity: `${bucketSize}_${bucketUnit}`
-    });
-  },
-
-  /**
-   * Fetch alert data for each function id that the currently selected group watches
-   * @method fetchAlertDataById
-   * @param {Object} functionIds - alert ids included in the currently selected group
-   * @return {RSVP.hash} A new list of functions (alerts)
-   */
-  fetchAlertDataById: task(function * (functionIds) {
-    const functionArray = yield functionIds.map(id => fetch(selfServeApiCommon.alertById(id)).then(checkStatus));
-    return RSVP.hash(functionArray);
-  }),
-
-  actions: {
-    /**
-     * save session url for transition on login
-     * @method willTransition
-     */
-    willTransition(transition) {
-      //saving session url - TODO: add a util or service - lohuynh
-      if (transition.intent.name && transition.intent.name !== 'logout') {
-        this.set('session.store.fromUrl', {lastIntentTransition: transition});
-      }
-    },
-
-    /**
-     * Handle any errors occurring in model/afterModel in parent route
-     * https://www.emberjs.com/api/ember/2.16/classes/Route/events/error?anchor=error
-     * https://guides.emberjs.com/v2.18.0/routing/loading-and-error-substates/#toc_the-code-error-code-event
-     */
-    error() {
-      return true;
-    },
-
-    /**
-     * Toast confirmation of save status
-     */
-    confirmSaveStatus(isSuccess) {
-      const notifications = this.get('notifications');
-      const toastOptions = {
-        timeOut: '4000',
-        positionClass: 'toast-bottom-right'
-      };
-      if (isSuccess) {
-        notifications.success('Alert options saved successfully', 'Done', toastOptions);
-      } else {
-        notifications.error('Alert options failed to save', 'Error', toastOptions);
-      }
-    },
-
-    /**
-     * Action called on submission to reload the route's model
-     */
-    refreshModel() {
-      this.refresh();
-    },
-
-    /**
-    * Refresh anomaly data when changes are made
-    */
-    loadFunctionsTable(selectedConfigGroup) {
-      get(this, 'prepareAlertList').perform(selectedConfigGroup);
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/template.hbs
deleted file mode 100644
index 2d07114..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/edit/template.hbs
+++ /dev/null
@@ -1,86 +0,0 @@
-<main class="alert-create card-container card-container--padded te-form">
-  <fieldset class="te-form__section row">
-    <div class="col-xs-12">
-      <legend class="te-form__section-title">Alert Details</legend>
-    </div>
-
-    {{!-- Field: Alert Name --}}
-    <div class="form-group col-xs-10">
-      <label for="anomaly-form-function-name" class="control-label te-label required">
-        Alert Name
-        <div class="te-form__sub-label">(Please follow this naming convention: <span class="te-form__sub-label--strong">productName_metricName_dimensionName_other</span>)</div>
-      </label>
-      {{#if isAlertNameDuplicate}}
-        <div class="te-form__alert--warning alert-warning">Warning: <strong>{{alertFunctionName}}</strong> already exists. Please try another name.</div>
-      {{/if}}
-      {{input
-        type="text"
-        id="anomaly-form-function-name"
-        class="form-control te-input te-input--read-only"
-        placeholder="Add a descriptive alert name"
-        value=alertFunctionName
-        key-up=(action "validateAlertName" alertFunctionName)
-      }}
-    </div>
-
-    {{!-- Field: Active --}}
-    <div class="form-group col-xs-2">
-      <label for="select-status" class="control-label te-label required">
-        Status
-        <div class="te-form__sub-label">Toggles detection on/off</div>
-      </label>
-      {{#x-toggle
-        value=isActive
-        classNames="te-toggle te-toggle--form te-toggle--left"
-        theme='ios'
-        showLabels=true
-        name="activeToggle"
-        onToggle=(action (mut isActive))
-      as |toggle|}}
-        {{#toggle.label value=isActive}}
-          <span class="te-label te-label--flush">{{if isActive 'Active' 'Inactive'}}</span>
-        {{/toggle.label}}
-        {{toggle.switch theme='ios' onLabel='diff on' offLabel='diff off'}}
-      {{/x-toggle}}
-    </div>
-  </fieldset>
-
-  <fieldset class="te-form__section row">
-    <div class="col-xs-12">
-      <legend class="te-form__section-title">Notification Settings</legend>
-    </div>
-
-    {{!-- Button: Edit --}}
-    <div class="form-group col-xs-12">
-      Alerts can be part of multiple different subscription groups. Each group will send out the alert once according to schedule.
-      <div>
-        <button {{action "onShowManageGroupsModal"}}>Edit Notification Settings</button>
-      </div>
-    </div>
-  </fieldset>
-
-  <fieldset class="te-form__section-submit">
-    {{bs-button
-      type="outline-primary"
-      buttonType="Cancel"
-      defaultText="Cancel"
-      onClick=(action "onCancel")
-      class="te-button te-button--cancel"
-    }}
-
-    {{bs-button
-      defaultText="Save"
-      type="primary"
-      onClick=(action "onSubmit")
-      buttonType="submit"
-      disabled=isSubmitDisabled
-      class="te-button te-button--submit"
-    }}
-
-  </fieldset>
-</main>
-
-{{modals/manage-groups-modal
-  showManageGroupsModal=showManageGroupsModal
-  preselectedFunctionId=alertId
-}}
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/controller.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/controller.js
deleted file mode 100644
index 2a9dcf8..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/controller.js
+++ /dev/null
@@ -1,816 +0,0 @@
-/**
- * Controller for Alert Details Page: Overview Tab
- * @module manage/alert/explore
- * @exports manage/alert/explore
- */
-import _ from 'lodash';
-import fetch from 'fetch';
-import moment from 'moment';
-import { later } from "@ember/runloop";
-import { isPresent } from "@ember/utils";
-import Controller from '@ember/controller';
-import { task, timeout } from 'ember-concurrency';
-import {
-  set,
-  get,
-  computed,
-  setProperties,
-  getProperties,
-  getWithDefault
-} from '@ember/object';
-import {
-  checkStatus,
-  postProps,
-  buildDateEod
-} from 'thirdeye-frontend/utils/utils';
-import {
-  buildAnomalyStats,
-  extractSeverity
-} from 'thirdeye-frontend/utils/manage-alert-utils';
-import { inject as service } from '@ember/service';
-import config from 'thirdeye-frontend/config/environment';
-import floatToPercent from 'thirdeye-frontend/utils/float-to-percent';
-import * as anomalyUtil from 'thirdeye-frontend/utils/anomaly';
-
-export default Controller.extend({
-  /**
-   * Be ready to receive time span for anomalies via query params
-   */
-  queryParams: ['duration', 'startDate', 'endDate', 'repRunStatus'],
-  duration: null,
-  startDate: null,
-  endDate: null,
-  repRunStatus: null,
-  openReport: false,
-
-  /**
-   * Mapping anomaly table column names to corresponding prop keys
-   */
-  sortMap: {
-    number: 'index',
-    start: 'anomalyStart',
-    score: 'severityScore',
-    change: 'changeRate',
-    resolution: 'anomalyFeedback'
-  },
-
-  /**
-   * Make duration service accessible
-   */
-  durationCache: service('services/duration'),
-
-  /**
-   * Date format for date range picker
-   */
-  serverDateFormat: 'YYYY-MM-DD HH:mm',
-
-  /**
-   * Set initial view values
-   * @method initialize
-   * @param {Boolean} isReplayNeeded
-   * @return {undefined}
-   */
-  initialize() {
-    const {
-      repRunStatus,
-      openReport
-    } = this.getProperties('repRunStatus', 'openReport');
-
-    this.setProperties({
-      filters: {},
-      loadedWowData: [],
-      topDimensions: [],
-      predefinedRanges: {},
-      missingAnomalyProps: {},
-      selectedSortMode: '',
-      replayErrorMailtoStr: '',
-      selectedTimeRange: '',
-      selectedFilters: JSON.stringify({}),
-      timePickerIncrement: 5,
-      renderStatusIcon: true,
-      openReportModal: false,
-      isAlertReady: false,
-      isGraphReady: false,
-      isReportSuccess: false,
-      isReportFailure: false,
-      isPageLoadFailure: false,
-      isAnomalyArrayChanged: false,
-      sortColumnStartUp: false,
-      sortColumnScoreUp: false,
-      sortColumnChangeUp: false,
-      sortColumnNumberUp: true,
-      isAnomalyListFiltered: false,
-      isDimensionFetchDone: false,
-      sortColumnResolutionUp: false,
-      checkReplayInterval: 2000, // 2 seconds
-      selectedDimension: 'All Dimensions',
-      selectedResolution: 'All Resolutions',
-      labelMap: anomalyUtil.anomalyResponseMap,
-      currentPage: 1,
-      pageSize: 10
-    });
-
-    // Start checking for replay to end if a jobId is present
-    if (this.get('isReplayPending')) {
-      this.set('replayStartTime', moment());
-      this.get('checkReplayStatus').perform(this.get('jobId'));
-    }
-
-    // If a replay is still running, reload when done
-    if (repRunStatus) {
-      this.get('checkForNewAnomalies').perform(repRunStatus);
-    }
-
-    // If query param is set, auto-open report anomaly modal
-    if (openReport) {
-      this.triggerOpenReportModal();
-    }
-  },
-
-  /**
-   * newLink: Id of new alert (for migrated alerts)
-   * @type {String}
-   */
-  newId: computed(
-    'alertData',
-    function() {
-      const alertData = get(this, 'alertData');
-      if(alertData && alertData.functionName) {
-        let pieces = alertData.functionName.split('_');
-        if (pieces.length > 0) {
-          return pieces[pieces.length-1];
-        }
-      }
-      return null;
-    }
-  ),
-
-  /**
-   * Table pagination: number of pages to display
-   * @type {Number}
-   */
-  paginationSize: computed(
-    'pagesNum',
-    'pageSize',
-    function() {
-      const { pagesNum, pageSize } = this.getProperties('pagesNum', 'pageSize');
-      return Math.min(pagesNum, pageSize/2);
-    }
-  ),
-
-  /**
-   * Table pagination: total Number of pages to display
-   * @type {Number}
-   */
-  pagesNum: computed(
-    'filteredAnomalies',
-    'pageSize',
-    function() {
-      const { filteredAnomalies, pageSize } = this.getProperties('filteredAnomalies', 'pageSize');
-      const anomalyCount = filteredAnomalies.length || 0;
-      return Math.ceil(anomalyCount/pageSize);
-    }
-  ),
-
-  /**
-   * Table pagination: creates the page Array for view
-   * @type {Array}
-   */
-  viewPages: computed(
-    'pages',
-    'currentPage',
-    'paginationSize',
-    'pagesNum',
-    function() {
-      const {
-        currentPage,
-        pagesNum: max,
-        paginationSize: size
-      } = this.getProperties('currentPage', 'pagesNum', 'paginationSize');
-      const step = Math.floor(size / 2);
-
-      if (max === 1) { return; }
-
-      const startingNumber = ((max - currentPage) < step)
-        ? Math.max(max - size + 1, 1)
-        : Math.max(currentPage - step, 1);
-
-      return [...new Array(size)].map((page, index) => startingNumber + index);
-    }
-  ),
-
-  /**
-   * Table pagination: pre-filtered and sorted anomalies with pagination
-   * @type {Array}
-   */
-  paginatedFilteredAnomalies: computed(
-    'filteredAnomalies.@each',
-    'pageSize',
-    'currentPage',
-    'loadedWoWData',
-    'selectedSortMode',
-    'selectedResolution',
-    function() {
-      let anomalies = this.get('filteredAnomalies');
-      const { pageSize, currentPage, selectedSortMode } = getProperties(this, 'pageSize', 'currentPage', 'selectedSortMode');
-
-      if (selectedSortMode) {
-        let [ sortKey, sortDir ] = selectedSortMode.split(':');
-
-        if (sortDir === 'up') {
-          anomalies = anomalies.sortBy(this.get('sortMap')[sortKey]);
-        } else {
-          anomalies = anomalies.sortBy(this.get('sortMap')[sortKey]).reverse();
-        }
-      }
-
-      return anomalies.slice((currentPage - 1) * pageSize, currentPage * pageSize);
-    }
-  ),
-
-  /**
-   * date-time-picker: indicates the date format to be used based on granularity
-   * @type {String}
-   */
-  uiDateFormat: computed('alertData.windowUnit', function() {
-    const rawGranularity = this.get('alertData.bucketUnit');
-    const granularity = rawGranularity ? rawGranularity.toLowerCase() : '';
-
-    switch(granularity) {
-      case 'days':
-        return 'MMM D, YYYY';
-      case 'hours':
-        return 'MMM D, YYYY h a';
-      default:
-        return 'MMM D, YYYY hh:mm a';
-    }
-  }),
-
-  /**
-   * Preps a mailto link containing the currently selected metric name
-   * @method graphMailtoLink
-   * @return {String} the URI-encoded mailto link
-   */
-  graphMailtoLink: computed(
-    'alertData',
-    function() {
-      const alertData = this.get('alertData');
-      const fullMetricName = `${alertData.collection}::${alertData.metric}`;
-      const recipient = config.email;
-      const subject = 'TE Self-Serve Alert Page: error loading metric and/or alert records';
-      const body = `TE Team, please look into a possible inconsistency issue with [ ${fullMetricName} ] in alert page for alert id ${alertData.id}
-                    Alert page: ${location.href}`;
-      const mailtoString = `mailto:${recipient}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
-      return mailtoString;
-    }
-  ),
-
-  /**
-   * Determines whether there is a discrepancy between anomaly ids detected and anomaly records loaded
-   * @type {Boolean}
-   */
-  isAnomalyLoadError: computed(
-    'totalAnomalies',
-    'anomalyData.length',
-    function() {
-      const { totalAnomalies, anomalyData } = getProperties(this, 'totalAnomalies', 'anomalyData');
-      const totalsMatching = anomalyData ? (totalAnomalies !== anomalyData.length) : true;
-      return totalsMatching;
-    }
-  ),
-
-  /**
-   * Data needed to render the stats 'cards' above the anomaly graph for this alert
-   * @type {Object}
-   */
-  anomalyStats: computed(
-    'alertData',
-    'alertEvalMetrics',
-    'alertEvalMetrics.projected',
-    function() {
-      const {
-        alertData,
-        alertEvalMetrics,
-        DEFAULT_SEVERITY: defaultSeverity
-      } = this.getProperties('alertData', 'alertEvalMetrics', 'DEFAULT_SEVERITY');
-      const features = getWithDefault(alertData, 'alertFilter.features', null);
-      const mttdStr = _.has(alertData, 'alertFilter.mttd') ? alertData.alertFilter.mttd.split(';') : null;
-      const severityUnit = (!mttdStr || mttdStr && mttdStr[1].split('=')[0] !== 'deviation') ? '%' : '';
-      const mttdWeight = Number(extractSeverity(alertData, defaultSeverity));
-      const convertedWeight = severityUnit === '%' ? mttdWeight * 100 : mttdWeight;
-      const statsCards = [
-        {
-          title: 'Number of anomalies',
-          key: 'totalAlerts',
-          tooltip: false,
-          hideProjected: false,
-          text: 'Actual number of alerts sent'
-        },
-        {
-          title: 'Review Rate',
-          key: 'responseRate',
-          units: '%',
-          tooltip: false,
-          hideProjected: true,
-          text: '% of anomalies that are reviewed.'
-        },
-        {
-          title: 'Precision',
-          key: 'precision',
-          units: '%',
-          tooltip: false,
-          text: '% of true anomalies among alerted anomalies.'
-        },
-        {
-          title: 'Recall',
-          key: 'recall',
-          units: '%',
-          tooltip: false,
-          text: '% of true alerted anomalies among the total true anomalies.'
-        },
-        {
-          title: `MTTD for > ${convertedWeight}${severityUnit} change`,
-          key: 'mttd',
-          units: 'hrs',
-          tooltip: false,
-          hideProjected: true,
-          text: `Minimum time to detect for anomalies with > ${convertedWeight}${severityUnit} change`
-        }
-      ];
-
-      return buildAnomalyStats(alertEvalMetrics, statsCards, true);
-    }
-  ),
-
-  /**
-   * If user selects a dimension from the dropdown, we filter the anomaly results here.
-   * NOTE: this is currently set up to support single-dimension filters
-   * @type {Object}
-   */
-  filteredAnomalies: computed(
-    'selectedDimension',
-    'selectedResolution',
-    'anomalyData',
-    'anomaliesLoaded',
-    function() {
-      const {
-        labelMap,
-        anomalyData,
-        anomaliesLoaded,
-        selectedDimension: targetDimension,
-        selectedResolution: targetResolution
-      } = this.getProperties('labelMap', 'anomalyData', 'selectedDimension', 'selectedResolution', 'anomaliesLoaded');
-      let newAnomalies = [];
-
-      if (anomaliesLoaded  && anomalyData) {
-        newAnomalies = this.get('anomalyData');
-        if (targetDimension !== 'All Dimensions') {
-          // Filter for selected dimension
-          newAnomalies = newAnomalies.filter(data => targetDimension === data.dimensionString);
-        }
-        if (targetResolution !== 'All Resolutions') {
-          // Filter for selected resolution
-          newAnomalies = newAnomalies.filter(data => targetResolution === labelMap[data.anomalyFeedback]);
-        }
-        // Let page know whether anomalies viewed are filtered or not
-        set(this, 'isAnomalyListFiltered', anomalyData.length !== newAnomalies.length);
-        // Add an index number to each row
-        newAnomalies.forEach((anomaly, index) => {
-          set(anomaly, 'index', ++index);
-        });
-      }
-      return newAnomalies;
-    }
-  ),
-
-  /**
-   * All selected dimensions to be loaded into graph
-   * @returns {Array}
-   */
-  selectedDimensions: computed(
-    'topDimensions',
-    'topDimensions.@each.isSelected',
-    function() {
-      return this.get('topDimensions').filterBy('isSelected');
-    }
-  ),
-
-  /**
-   * Find the active baseline option name
-   * @type {String}
-   */
-  baselineTitle: computed(
-    'baselineOptions',
-    function() {
-      const activeOpName = this.get('baselineOptions').filter(item => item.isActive)[0].name;
-      const displayName = `Current/${activeOpName}`;
-      return displayName;
-    }
-  ),
-
-  /**
-   * Generate date range selection options if needed
-   * @method renderDate
-   * @param {Number} range - number of days (duration)
-   * @return {String}
-   */
-  renderDate(range) {
-    // TODO: enable single day range
-    const newDate = buildDateEod(range, 'days').format("DD MM YYY");
-    return `Last ${range} Days (${newDate} to Today)`;
-  },
-
-  /**
-   * Concurrency task to ping the job-info endpoint to check status of an ongoing replay job.
-   * If there is no progress after a set time, we display an error message.
-   * @param {Number} jobId - the id for the newly triggered replay job
-   * @return {undefined}
-   */
-  checkReplayStatus: task(function * (jobId) {
-    yield timeout(2000);
-
-    const {
-      alertId,
-      replayStartTime
-    } = this.getProperties('alertId', 'replayStartTime');
-    const replayStatusList = ['completed', 'failed', 'timeout'];
-    const checkStatusUrl = `/detection-onboard/get-status?jobId=${jobId}`;
-    let isReplayTimeUp = Number(moment.duration(moment().diff(replayStartTime)).asSeconds().toFixed(0)) > 60;
-
-    // In replay status check, continue to display "pending" banner unless we have known success or failure.
-    fetch(checkStatusUrl).then(checkStatus)
-      .then((jobStatus) => {
-        const replayStatusObj = _.has(jobStatus, 'taskStatuses')
-          ? jobStatus.taskStatuses.find(status => status.taskName === 'FunctionReplay')
-          : null;
-        const replayStatus = replayStatusObj ? replayStatusObj.taskStatus.toLowerCase() : '';
-        // When either replay is no longer pending or 60 seconds have passed, transition to full alert page.
-        if (replayStatusList.includes(replayStatus) || isReplayTimeUp) {
-          const repRunStatus = replayStatus === 'running' ? jobId : null;
-          // Replay may be complete. Give server time to load anomalies
-          later(this, function() {
-            this.transitionToRoute('manage.alert', alertId, { queryParams: { jobId: null, repRunStatus }});
-          }, 3000);
-        } else {
-          this.get('checkReplayStatus').perform(jobId);
-        }
-      })
-      .catch(() => {
-        // If we have job status failure, go ahead and transition to full alert page.
-        this.transitionToRoute('manage.alert', alertId, { queryParams: { jobId: null }});
-      });
-  }),
-
-  /**
-   * Concurrency task to reload page once a running replay is complete
-   * @param {Number} jobId - the id for the newly triggered replay job
-   * @return {undefined}
-   */
-  checkForNewAnomalies: task(function * (jobId) {
-    yield timeout(5000);
-
-    // In replay status check, continue to display "pending" banner unless we have known success or failure.
-    fetch(`/detection-onboard/get-status?jobId=${jobId}`).then(checkStatus)
-      .then((jobStatus) => {
-        const replayStatusObj = _.has(jobStatus, 'taskStatuses')
-          ? jobStatus.taskStatuses.find(status => status.taskName === 'FunctionReplay')
-          : null;
-        if (replayStatusObj) {
-          if (replayStatusObj.taskStatus.toLowerCase() === 'completed') {
-            this.transitionToRoute({ queryParams: { repRunStatus: null }});
-          } else {
-            this.get('checkForNewAnomalies').perform(jobId);
-          }
-        }
-      })
-      .catch(() => {
-        // If we have job status failure, go ahead and transition to full alert page.
-        this.transitionToRoute('manage.alert', this.get('alertId'), { queryParams: { repRunStatus: null }});
-      });
-  }),
-
-  /**
-   * Send a POST request to the report anomaly API (2-step process)
-   * http://go/te-ss-alert-flow-api
-   * @method reportAnomaly
-   * @param {String} id - The alert id
-   * @param {Object} data - The input values from 'report new anomaly' modal
-   * @return {Promise}
-   */
-  reportAnomaly(id, data) {
-    const reportUrl = `/anomalies/reportAnomaly/${id}?`;
-    const updateUrl = `/anomalies/updateFeedbackRange/${data.startTime}/${data.endTime}/${id}?feedbackType=${data.feedbackType}`;
-    const requiredProps = ['data.startTime', 'data.endTime', 'data.feedbackType'];
-    const missingData = !requiredProps.every(prop => isPresent(prop));
-    let queryStringUrl = reportUrl;
-
-    if (missingData) {
-      return Promise.reject(new Error('missing data'));
-    } else {
-      Object.entries(data).forEach(([key, value]) => {
-        queryStringUrl += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
-      });
-      // Step 1: Report the anomaly
-      return fetch(queryStringUrl, postProps('')).then((res) => checkStatus(res, 'post'))
-        .then(() => {
-          // Step 2: Automatically update anomaly feedback in that range
-          return fetch(updateUrl, postProps('')).then((res) => checkStatus(res, 'post'));
-        });
-    }
-  },
-
-  /**
-   * Modal opener for "report missing anomaly". Can be triggered from link click or
-   * automatically via queryparam "openReport=true"
-   * @method triggerOpenReportModal
-   * @return {undefined}
-   */
-  triggerOpenReportModal() {
-    this.setProperties({
-      isReportSuccess: false,
-      isReportFailure: false,
-      openReportModal: true
-    });
-    // We need the C3/D3 graph to render after its containing parent elements are rendered
-    // in order to avoid strange overflow effects.
-    later(() => {
-      this.set('renderModalContent', true);
-    });
-  },
-
-  /**
-   * When exiting route, lets kill the replay status check calls
-   * @method clearAll
-   * @return {undefined}
-   */
-  clearAll() {
-    this.setProperties({
-      activeRangeStart: '',
-      activeRangeEnd: '',
-      alertEvalMetrics: {}
-    });
-    // Cancel controller concurrency tasks
-    this.get('checkReplayStatus').cancelAll();
-    this.get('checkForNewAnomalies').cancelAll();
-  },
-
-  /**
-   * Actions for alert page
-   */
-  actions: {
-
-    /**
-     * Handle selected dimension filter
-     * @method onSelectDimension
-     * @param {Object} selectedObj - the user-selected dimension to filter by
-     */
-    onSelectDimension(selectedObj) {
-      setProperties(this, {
-        currentPage: 1,
-        selectedDimension: selectedObj
-      });
-      // Select graph dimensions based on filter
-      this.get('topDimensions').forEach((dimension) => {
-        const isAllSelected = selectedObj === 'All Dimensions';
-        const isActive = selectedObj.includes(dimension.name) || isAllSelected;
-        set(dimension, 'isSelected', isActive);
-      });
-    },
-
-    /**
-     * Handle selected resolution filter
-     * @method onSelectResolution
-     * @param {Object} selectedObj - the user-selected resolution to filter by
-     */
-    onSelectResolution(selectedObj) {
-      setProperties(this, {
-        currentPage: 1,
-        selectedResolution: selectedObj
-      });
-    },
-
-    /**
-     * Handle dynamically saving anomaly feedback responses
-     * @method onChangeAnomalyResponse
-     * @param {Object} anomalyRecord - the anomaly being responded to
-     * @param {String} selectedResponse - user-selected anomaly feedback option
-     * @param {Object} inputObj - the selection object
-     */
-     onChangeAnomalyResponse: async function(anomalyRecord, selectedResponse, inputObj) {
-      const responseObj = anomalyUtil.anomalyResponseObj.find(res => res.name === selectedResponse);
-      const labelMap = get(this, 'labelMap');
-      const loadedResponsesArr = [];
-      const newOptionsArr = [];
-      // Update select field
-      set(inputObj, 'selected', selectedResponse);
-      // Reset status icon
-      set(this, 'renderStatusIcon', false);
-      try {
-        // Save anomaly feedback
-        await anomalyUtil.updateAnomalyFeedback(anomalyRecord.anomalyId, responseObj.value);
-        // We make a call to ensure our new response got saved
-        const anomaly = await anomalyUtil.verifyAnomalyFeedback(anomalyRecord.anomalyId, responseObj.status);
-        const filterMap = getWithDefault(anomaly, 'searchFilters.statusFilterMap', null);
-        // This verifies that the status change got saved as key in the anomaly statusFilterMap property
-        const keyPresent = filterMap && Object.keys(filterMap).find(key => responseObj.status.includes(key));
-        if (keyPresent) {
-          setProperties(anomalyRecord, {
-            anomalyFeedback: responseObj.status,
-            showResponseSaved: true,
-            showResponseFailed: false
-          });
-          // Collect all available new labels
-          loadedResponsesArr.push(responseObj.status, ...get(this, 'anomalyData').mapBy('anomalyFeedback'));
-          loadedResponsesArr.forEach((response) => {
-            if (labelMap[response]) { newOptionsArr.push(labelMap[response]) }
-          });
-          // Update resolutionOptions array - we may have a new option now
-          set(this, 'resolutionOptions', [ ...new Set([ 'All Resolutions', ...newOptionsArr ])]);
-        } else {
-          throw 'Response not saved';
-        }
-      } catch (err) {
-        setProperties(anomalyRecord, {
-          showResponseFailed: true,
-          showResponseSaved: false
-        });
-      }
-      // Force status icon to refresh
-      set(this, 'renderStatusIcon', true);
-    },
-
-    /**
-     * Action handler for page clicks
-     * @param {Number|String} page
-     */
-    onPaginationClick(page) {
-      let newPage = page;
-      let currentPage = this.get('currentPage');
-
-      switch (page) {
-        case 'previous':
-          if (currentPage > 1) {
-            newPage = --currentPage;
-          } else {
-            newPage = currentPage;
-          }
-          break;
-        case 'next':
-          if (currentPage < this.get('pagesNum')) {
-            newPage = ++currentPage;
-          } else {
-            newPage = currentPage;
-          }
-          break;
-      }
-
-      this.set('currentPage', newPage);
-    },
-
-    /**
-     * Handle submission of missing anomaly form from alert-report-modal
-     */
-    onSave() {
-      const { alertId, missingAnomalyProps } = this.getProperties('alertId', 'missingAnomalyProps');
-      this.reportAnomaly(alertId, missingAnomalyProps)
-        .then((result) => {
-          const rangeFormat = 'YYYY-MM-DD HH:mm';
-          const startStr = moment(missingAnomalyProps.startTime).format(rangeFormat);
-          const endStr = moment(missingAnomalyProps.endTime).format(rangeFormat);
-          this.setProperties({
-            isReportSuccess: true,
-            openReportModal: false,
-            reportedRange: `${startStr} - ${endStr}`
-          });
-          // Reload after save confirmation
-          later(this, function() {
-            this.send('refreshModel');
-          }, 1000);
-        })
-        // If failure, leave modal open and report
-        .catch((err) => {
-          this.setProperties({
-            missingAnomalyProps: {},
-            isReportFailure: true
-          });
-        });
-    },
-
-    /**
-     * Handle missing anomaly modal cancel
-     */
-    onCancel() {
-      this.setProperties({
-        isReportSuccess: false,
-        isReportFailure: false,
-        openReportModal: false,
-        renderModalContent: false
-      });
-    },
-
-    /**
-     * Open modal for missing anomalies
-     */
-    onClickReportAnomaly() {
-      this.triggerOpenReportModal();
-    },
-
-    /**
-     * Received bubbled-up action from modal
-     * @param {Object} all input field values
-     */
-    onInputMissingAnomaly(inputObj) {
-      this.set('missingAnomalyProps', inputObj);
-    },
-
-    /**
-     * Handle display of selected baseline options
-     * @param {Object} wowObj - the baseline selection
-     */
-    onBaselineOptionClick(wowObj) {
-      const { anomalyData, baselineOptions } = this.getProperties('anomalyData', 'baselineOptions');
-      const isValidSelection = !wowObj.isActive;
-      let newOptions = baselineOptions.map((val) => {
-        return { name: val.name, isActive: false };
-      });
-
-      // Set active option
-      newOptions.find((val) => val.name === wowObj.name).isActive = true;
-      this.set('baselineOptions', newOptions);
-
-      // Set new values for each anomaly
-      if (isValidSelection) {
-        anomalyData.forEach((anomaly) => {
-          const wow = anomaly.wowData;
-          const wowDetails = wow.compareResults.find(res => res.compareMode.toLowerCase() === wowObj.name.toLowerCase());
-          let curr = anomaly.current;
-          let base = anomaly.baseline;
-          let change = anomaly.changeRate;
-
-          if (wowDetails) {
-            curr = wow.currentVal.toFixed(2);
-            base = wowDetails.baselineValue.toFixed(2);
-            change = floatToPercent(wowDetails.change);
-          }
-
-          // Set displayed value properties. Note: ensure no CP watching these props
-          setProperties(anomaly, {
-            shownCurrent: curr,
-            shownBaseline: base,
-            shownChangeRate: change
-          });
-        });
-      }
-    },
-
-    /**
-     * Sets the new custom date range for anomaly coverage
-     * @method onRangeSelection
-     * @param {Object} rangeOption - the user-selected time range to load
-     */
-    onRangeSelection(rangeOption) {
-      const {
-        start,
-        end,
-        value: duration
-      } = rangeOption;
-      const durationObj = {
-        duration,
-        startDate: moment(start).valueOf(),
-        endDate: moment(end).valueOf()
-      };
-      // Cache the new time range and update page with it
-      this.get('durationCache').setDuration(durationObj);
-      this.transitionToRoute({ queryParams: durationObj });
-    },
-
-
-    /**
-     * Load tuning sub-route and properly toggle alert nav button
-     */
-    onClickTuneSensitivity() {
-      this.send('updateParentLink');
-      const { duration, startDate, endDate } = this.model;
-      this.transitionToRoute('manage.alert.tune', this.get('alertId'), { queryParams: { duration, startDate, endDate }});
-    },
-
-    /**
-     * Handle sorting for each sortable table column
-     * @param {String} sortKey  - stringified start date
-     */
-    toggleSortDirection(sortKey) {
-      const propName = 'sortColumn' + sortKey.capitalize() + 'Up' || '';
-
-      this.toggleProperty(propName);
-      if (this.get(propName)) {
-        this.set('selectedSortMode', sortKey + ':up');
-      } else {
-        this.set('selectedSortMode', sortKey + ':down');
-      }
-
-      //On sort, set table to first pagination page
-      this.set('currentPage', 1);
-    }
-
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/route.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/route.js
deleted file mode 100644
index c67b6a3..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/route.js
+++ /dev/null
@@ -1,605 +0,0 @@
-/**
- * Handles the 'explore' route for manage alert
- * @module manage/alert/edit/explore
- * @exports manage/alert/edit/explore
- */
-import RSVP from 'rsvp';
-import fetch from 'fetch';
-import moment from 'moment';
-import Route from '@ember/routing/route';
-import { later } from '@ember/runloop';
-import { task, timeout } from 'ember-concurrency';
-import { inject as service } from '@ember/service';
-import {
-  get,
-  setProperties
-} from '@ember/object';
-import { isPresent, isNone, isBlank } from '@ember/utils';
-import {
-  checkStatus,
-  buildDateEod,
-  makeFilterString,
-  toIso
-} from 'thirdeye-frontend/utils/utils';
-import {
-  enhanceAnomalies,
-  toIdGroups,
-  setUpTimeRangeOptions,
-  getTopDimensions,
-  buildMetricDataUrl,
-  extractSeverity
-} from 'thirdeye-frontend/utils/manage-alert-utils';
-import {
-  selfServeApiCommon,
-  selfServeApiGraph
-} from 'thirdeye-frontend/utils/api/self-serve';
-import {
-  anomalyResponseObj,
-  anomalyResponseMap
-} from 'thirdeye-frontend/utils/anomaly';
-import { getAnomalyDataUrl } from 'thirdeye-frontend/utils/api/anomaly';
-
-/**
- * Shorthand for setting date defaults
- */
-const displayDateFormat = 'YYYY-MM-DD HH:mm';
-
-/**
- * Basic alert page constants
- */
-const DEFAULT_SEVERITY = 0.3;
-const DIMENSION_COUNT = 7;
-const METRIC_DATA_COLOR = 'blue';
-
-/**
- * Basic alert page defaults
- */
-const wowOptions = ['Wow', 'Wo2W', 'Wo3W', 'Wo4W'];
-const durationMap = { m:'month', d:'day', w:'week' };
-const baselineOptions = [{ name: 'Predicted', isActive: true }];
-const defaultDurationObj = {
-  duration: '3m',
-  startDate: buildDateEod(3, 'month').valueOf(),
-  endDate: moment()
-};
-
-/**
- * Build WoW array from basic options
- */
-const newWowList = wowOptions.map((item) => {
-  return { name: item, isActive: false };
-});
-
-/**
- * Derives start/end timestamps based on queryparams and user-selected time range with certain fall-backs/defaults
- * @param {String} bucketUnit - is requested range from an hourly or minutely metric?
- * @param {String} duration - the model's processed query parameter for duration ('1m', '2w', etc)
- * @param {String} start - the model's processed query parameter for startDate
- * @param {String} end - the model's processed query parameter for endDate
- * @returns {Object}
- */
-const processRangeParams = (bucketUnit = 'DAYS', duration, start, end) => {
-  // To avoid loading too much data, override our time span defaults based on whether the metric is 'minutely'
-  const isMetricMinutely = bucketUnit.toLowerCase().includes('minute');
-  const defaultQueryUnit = isMetricMinutely ? 'week' : 'month';
-  const defaultQuerySize = isMetricMinutely ? 2 : 1;
-
-  // We also allow a 'duration' query param to set the time range. For example, duration=15d (last 15 days)
-  const qsRegexMatch = duration.match(new RegExp(/^(\d)+([d|m|w])$/i));
-  const durationMatch = duration && qsRegexMatch ? qsRegexMatch : [];
-
-  // If the duration string is recognized, we use it. Otherwise, we fall back on the defaults above
-  const querySize = durationMatch && durationMatch.length ? durationMatch[1] : defaultQuerySize;
-  const queryUnit = durationMatch && durationMatch.length ? durationMap[durationMatch[2].toLowerCase()] : defaultQueryUnit;
-
-  // If duration = 'custom', we know the user is requesting specific start/end times.
-  // In this case, we will use those instead of our parsed duration & defaults
-  const isCustomDate = duration === 'custom';
-  const baseStart = isCustomDate ? moment(parseInt(start, 10)) : buildDateEod(querySize, queryUnit);
-  const baseEnd = isCustomDate ? moment(parseInt(end, 10)) : moment();
-
-  // These resulting timestamps are used for our graph and anomaly queries
-  const startStamp = baseStart.valueOf();
-  const endStamp = baseEnd.valueOf();
-
-  return { startStamp, endStamp, baseStart, baseEnd };
-};
-
-/**
- * Setup for query param behavior
- */
-const queryParamsConfig = {
-  refreshModel: true,
-  replace: false
-};
-
-export default Route.extend({
-  queryParams: {
-    duration: queryParamsConfig,
-    startDate: queryParamsConfig,
-    endDate: queryParamsConfig,
-    openReport: queryParamsConfig,
-    repRunStatus: queryParamsConfig
-  },
-
-  /**
-   * Make duration service accessible
-   */
-  durationCache: service('services/duration'),
-  session: service(),
-
-  beforeModel(transition) {
-    const { duration, startDate } = transition.queryParams;
-    // Default to 1 month of anomalies to show if no dates present in query params
-    if (!duration || !startDate) {
-      this.transitionTo({ queryParams: defaultDurationObj });
-    }
-  },
-
-  model(params, transition) {
-    const { id, alertData, jobId } = this.modelFor('manage.alert');
-    const isReplayDone = isNone(jobId) && jobId !== -1;
-    if (!id) { return; }
-
-    // Get duration data from service
-    const {
-      duration,
-      startDate,
-      endDate
-    } = this.get('durationCache').getDuration(transition.queryParams, defaultDurationObj);
-
-    // Prepare endpoints for eval, mttd, projected metrics calls
-    const dateParams = `start=${toIso(startDate)}&end=${toIso(endDate)}`;
-    const evalUrl = `/detection-job/eval/filter/${id}?${dateParams}`;
-    const mttdUrl = `/detection-job/eval/mttd/${id}?severity=${extractSeverity(alertData, DEFAULT_SEVERITY)}`;
-    let performancePromiseHash = {
-      current: {},
-      projected: {},
-      mttd: ''
-    };
-
-    // Once replay is done or timed out, this route loads all needed data. We load placeholders first.
-    if (isReplayDone) {
-      performancePromiseHash = {
-        current: fetch(`${evalUrl}&isProjected=FALSE`).then(checkStatus),
-        projected: fetch(`${evalUrl}&isProjected=TRUE`).then(checkStatus),
-        mttd: fetch(mttdUrl).then(checkStatus)
-      };
-    }
-
-    return RSVP.hash(performancePromiseHash)
-      .then((alertEvalMetrics) => {
-        Object.assign(alertEvalMetrics.current, { mttd: alertEvalMetrics.mttd});
-        return {
-          id,
-          jobId,
-          alertData,
-          duration,
-          startDate,
-          evalUrl,
-          endDate,
-          dateParams,
-          alertEvalMetrics,
-          isReplayDone
-        };
-      })
-      // Catch is not mandatory here due to our error action, but left it to add more context.
-      .catch((error) => {
-        return RSVP.reject({ error, location: `${this.routeName}:model`, calls: performancePromiseHash });
-      });
-  },
-
-  afterModel(model) {
-    this._super(model);
-
-    const {
-      id: alertId,
-      alertData,
-      isReplayDone,
-      startDate,
-      endDate,
-      duration,
-      dateParams,
-      alertEvalMetrics
-    } = model;
-
-    // Pull alert properties into context
-    const {
-      metric: metricName,
-      collection: dataset,
-      exploreDimensions,
-      filters: filtersRaw,
-      bucketSize,
-      bucketUnit
-    } = alertData;
-    // Derive start/end time ranges based on querystring input with fallback on default '1 month'
-    const {
-      startStamp,
-      endStamp,
-      baseStart,
-      baseEnd
-    } = processRangeParams(bucketUnit, duration, startDate, endDate);
-
-    // Set initial value for metricId for early transition cases
-    const config = {
-      startStamp,
-      endStamp,
-      bucketSize,
-      bucketUnit,
-      baseEnd,
-      baseStart,
-      exploreDimensions,
-      filters: filtersRaw ? makeFilterString(filtersRaw) : ''
-    };
-
-    // Load endpoints for projected metrics. TODO: consolidate into CP if duplicating this logic
-    const anomalyDataUrl = getAnomalyDataUrl(startStamp, endStamp);
-    const metricsUrl = selfServeApiCommon.metricAutoComplete(metricName);
-    const anomaliesUrl = `/dashboard/anomaly-function/${alertId}/anomalies?${dateParams}&useNotified=true`;
-    let anomalyPromiseHash = {
-      projectedMttd: 0,
-      metricsByName: [],
-      anomalyIds: []
-    };
-
-    // If replay still pending, load placeholders for this data.
-    if (isReplayDone) {
-      anomalyPromiseHash = {
-        projectedMttd: 0, // In overview mode, no projected MTTD value is needed
-        metricsByName: fetch(metricsUrl).then(checkStatus),
-        anomalyIds: fetch(anomaliesUrl).then(checkStatus)
-      };
-    }
-
-    return RSVP.hash(anomalyPromiseHash)
-      .then(async (data) => {
-        const metricId = this._locateMetricId(data.metricsByName, alertData);
-        const totalAnomalies = data.anomalyIds.length;
-        Object.assign(alertEvalMetrics.projected, { mttd: data.projectedMttd });
-        Object.assign(config, { id: metricId });
-        Object.assign(model, {
-          anomalyIds: data.anomalyIds,
-          exploreDimensions,
-          totalAnomalies,
-          anomalyDataUrl,
-          anomaliesUrl,
-          config
-        });
-        const maxTimeUrl = selfServeApiGraph.maxDataTime(metricId);
-        const maxTime = isReplayDone && metricId ? await fetch(maxTimeUrl).then(checkStatus) : moment().valueOf();
-        Object.assign(model, { metricDataUrl: buildMetricDataUrl({
-          maxTime,
-          endStamp: config.endStamp,
-          startStamp: config.startStamp,
-          id: metricId,
-          filters: config.filters,
-          granularity: `${config.bucketSize}_${config.bucketUnit}`,
-          dimension: 'All' // NOTE: avoid dimension explosion - config.exploreDimensions ? config.exploreDimensions.split(',')[0] : 'All'
-        })});
-      })
-      // Catch is not mandatory here due to our error action, but left it to add more context
-      .catch((err) => {
-        return RSVP.reject({ err, location: `${this.routeName}:afterModel`, calls: anomalyPromiseHash });
-      });
-  },
-
-  setupController(controller, model) {
-    this._super(controller, model);
-
-    const {
-      id,
-      jobId,
-      alertData,
-      anomalyIds,
-      duration,
-      config,
-      loadError,
-      isReplayDone,
-      metricDataUrl,
-      anomalyDataUrl,
-      totalAnomalies,
-      exploreDimensions,
-      alertEvalMetrics
-    } = model;
-
-    // Prime the controller
-    controller.setProperties({
-      loadError,
-      jobId,
-      alertData,
-      alertId: id,
-      DEFAULT_SEVERITY,
-      totalAnomalies,
-      anomalyDataUrl,
-      baselineOptions,
-      alertEvalMetrics,
-      anomaliesLoaded: false,
-      isMetricDataInvalid: false,
-      isMetricDataLoading: true,
-      alertDimension: exploreDimensions,
-      isReplayPending: isPresent(jobId) && jobId !== -1,
-      alertHasDimensions: isPresent(exploreDimensions),
-      timeRangeOptions: setUpTimeRangeOptions(['3m'], duration),
-      baselineOptionsLoading: anomalyIds && anomalyIds.length > 0,
-      responseOptions: anomalyResponseObj.map(response => response.name)
-    });
-    // Kick off controller defaults and replay status check
-    controller.initialize();
-
-    // Ensure date range picker gets populated correctly
-    later(this, () => {
-      controller.setProperties({
-        activeRangeStart: moment(config.startStamp).format(displayDateFormat),
-        activeRangeEnd: moment(config.endStamp).format(displayDateFormat)
-      });
-    });
-
-    // Once replay is finished, begin loading anomaly and graph data as concurrency tasks
-    // See https://github.com/linkedin/pinot/pull/2518#discussion-diff-169751380R366
-    if (isReplayDone) {
-      get(this, 'loadAnomalyData').perform(anomalyIds, exploreDimensions);
-      get(this, 'loadGraphData').perform(metricDataUrl, exploreDimensions);
-    }
-  },
-
-  resetController(controller, isExiting) {
-    this._super(...arguments);
-
-    // Cancel all pending concurrency tasks in controller
-    if (isExiting) {
-      get(this, 'loadAnomalyData').cancelAll();
-      get(this, 'loadGraphData').cancelAll();
-      controller.clearAll();
-    }
-  },
-
-  /**
-   * Performs the repetitive task of setting graph properties based on
-   * returned metric data and dimension data
-   * @method _setGraphProperties
-   * @param {Object} metricData - returned metric timeseries data
-   * @param {String} exploreDimensions - string of metric dimensions
-   * @returns {undefined}
-   * @private
-   */
-  _setGraphProperties(metricData, exploreDimensions) {
-    const alertDimension = exploreDimensions ? exploreDimensions.split(',')[0] : '';
-    Object.assign(metricData, { color: METRIC_DATA_COLOR });
-    this.controller.setProperties({
-      metricData,
-      alertDimension,
-      isMetricDataLoading: false
-    });
-    // If alert has dimensions set, load them into graph once replay is done.
-    if (exploreDimensions && !this.controller.isReplayPending) {
-      const topDimensions = getTopDimensions(metricData, DIMENSION_COUNT);
-      this.controller.setProperties({
-        topDimensions,
-        isDimensionFetchDone: true,
-        availableDimensions: topDimensions.length
-      });
-    }
-  },
-
-  /**
-   * Tries find a specific metric id based on a common dataset string
-   * @method _locateMetricId
-   * @param {Array} metricList - list of metrics from metric-by-name lookup
-   * @param {Object} alertData - currently loaded alert properties
-   * @returns {Number} target metric id
-   * @private
-   */
-  _locateMetricId(metricList, alertData) {
-    const metricId = metricList.find((metric) => {
-      return (metric.name === alertData.metric) && (metric.dataset === alertData.collection);
-    }) || { id: 0 };
-    return isBlank(metricList) ? 0 : metricId.id;
-  },
-
-  /**
-   * Returns an aggregate list of all labels found in the currently-loaded anomaly set
-   * @method _filterResolutionLabels
-   * @param {Array} anomalyData - list of all anomalies for current alert
-   * @returns {Array} list of all labels found in anomaly set
-   * @private
-   */
-  _filterResolutionLabels(anomalyData) {
-    let availableLabels = [];
-    anomalyData.forEach((anomaly) => {
-      let mappedLabel = anomalyResponseMap[anomaly.anomalyFeedback];
-      if (mappedLabel) { availableLabels.push(mappedLabel); }
-    });
-    return availableLabels;
-  },
-
-  /**
-   * Fetches all anomaly data for found anomalies - downloads all 'pages' of data from server
-   * in order to handle sorting/filtering on the entire set locally. Start/end date are not used here.
-   * @param {Array} anomalyIds - list of all found anomaly ids
-   * @returns {RSVP promise}
-   */
-  fetchCombinedAnomalies: task(function * (anomalyIds) {
-    yield timeout(300);
-    if (anomalyIds.length) {
-      const idGroups = toIdGroups(anomalyIds);
-      const anomalyPromiseHash = idGroups.map((group, index) => {
-        let idStringParams = `anomalyIds=${encodeURIComponent(idGroups[index].toString())}`;
-        let url = `/anomalies/search/anomalyIds/0/0/${index + 1}?${idStringParams}`;
-        let getAnomalies = get(this, 'fetchAnomalyEntity').perform(url);
-        return RSVP.resolve(getAnomalies);
-      });
-      return RSVP.all(anomalyPromiseHash);
-    } else {
-      return RSVP.resolve([]);
-    }
-  }),
-
-  /**
-   * Fetches change rate data for each available anomaly id
-   * @method fetchCombinedAnomalyChangeData
-   * @param {Array} anomalyData - array of processed anomalies
-   * @returns {RSVP promise}
-   */
-  fetchCombinedAnomalyChangeData: task(function * (anomalyData) {
-    yield timeout(300);
-    let promises = [];
-
-    anomalyData.forEach((anomaly) => {
-      let id = anomaly.anomalyId;
-      promises[id] = get(this, 'fetchAnomalyEntity').perform(`/anomalies/${id}`);
-    });
-
-    return RSVP.hash(promises);
-  }),
-
-  /**
-   * Fetches severity scores for all anomalies
-   * TODO: Move this and other shared requests to a common service
-   * @param {Array} anomalyIds - list of all found anomaly ids
-   * @returns {RSVP promise}
-   */
-  fetchSeverityScores: task(function * (anomalyIds) {
-    yield timeout(300);
-    if (anomalyIds && anomalyIds.length) {
-      const anomalyPromiseHash = anomalyIds.map((id) => {
-        return RSVP.hash({
-          id,
-          score: get(this, 'fetchAnomalyEntity').perform(`/dashboard/anomalies/score/${id}`)
-        });
-      });
-      return RSVP.allSettled(anomalyPromiseHash);
-    } else {
-      return RSVP.resolve([]);
-    }
-  }),
-
-  /**
-   * Fetch any single entity as a cancellable concurrency task
-   * @param {String} url - endpoint for fetch
-   * @returns {fetch promise}
-   */
-  fetchAnomalyEntity: task(function * (url) {
-    yield timeout(300);
-    return fetch(url).then(checkStatus);
-  }),
-
-  /**
-   * Fetch all anomalies we have Ids for. Enhance the data and populate power-select filter options.
-   * Using ember concurrency parent/child tasks. When parent is cancelled, so are children
-   * http://ember-concurrency.com/docs/child-tasks.
-   * TODO: complete concurrency task error handling and refactor child tasks for cuncurrency.
-   * @param {Array} anomalyIds - the IDs of anomalies that have been reported for this alert.
-   * @return {undefined}
-   */
-  loadAnomalyData: task(function * (anomalyIds, exploreDimensions) {
-    const dimensionOptions = ['All Dimensions'];
-    const hasDimensions = exploreDimensions && exploreDimensions.length;
-    // Load data for each anomaly Id
-    const rawAnomalies = yield get(this, 'fetchCombinedAnomalies').perform(anomalyIds);
-    // Fetch and append severity score to each anomaly record
-    const severityScores = yield get(this, 'fetchSeverityScores').perform(anomalyIds);
-    // Process anomaly records to make them template-ready
-    const anomalyData = yield enhanceAnomalies(rawAnomalies, severityScores);
-    // Prepare de-duped power-select option array for anomaly feedback
-    const resolutionOptions = ['All Resolutions', ...new Set(this._filterResolutionLabels(anomalyData))];
-    // Populate dimensions power-select options if dimensions exist
-    if (hasDimensions) {
-      dimensionOptions.push(...new Set(anomalyData.map(anomaly => anomaly.dimensionString)));
-    }
-    // Push anomaly data into controller
-    this.controller.setProperties({
-      anomalyData,
-      dimensionOptions,
-      resolutionOptions,
-      anomaliesLoaded: true,
-      totalLoadedAnomalies: anomalyData.length,
-      baselineOptionsLoading: false
-    });
-    // Fetch and append extra WoW data for each anomaly record
-    const wowData = yield get(this, 'fetchCombinedAnomalyChangeData').perform(anomalyData);
-    anomalyData.forEach((anomaly) => {
-      anomaly.wowData = wowData[anomaly.anomalyId] || {};
-    });
-    // Load enhanced dataset into controller (WoW options will appear)
-    this.controller.setProperties({
-      anomalyData,
-      baselineOptions: [baselineOptions[0], ...newWowList]
-    });
-  // We use .cancelOn('deactivate') to make sure the task cancels when the user leaves the route.
-  // We use restartable to ensure that only one instance of the task is running at a time, hence
-  // any time setupController performs the task, any prior instances are canceled.
-  }).cancelOn('deactivate').restartable(),
-
-  /**
-   * Concurrenty task to ping the job-info endpoint to check status of an ongoing replay job.
-   * If there is no progress after a set time, we display an error message.
-   * @param {Number} jobId - the id for the newly triggered replay job
-   * @param {String} functionName - user-provided new function name (used to validate creation)
-   * @return {undefined}
-   */
-  loadGraphData: task(function * (metricDataUrl, exploreDimensions) {
-    try {
-      // Fetch and load graph metric data from either local store or API
-      const metricData = yield fetch(metricDataUrl).then(checkStatus);
-      // Load graph with metric data from timeseries API
-      yield this._setGraphProperties(metricData, exploreDimensions);
-    } catch (e) {
-      this.controller.setProperties({
-        isMetricDataInvalid: true,
-        isMetricDataLoading: false
-      });
-    }
-  }).cancelOn('deactivate').restartable(),
-
-  actions: {
-    /**
-     * save session url for transition on login
-     * @method willTransition
-     */
-    willTransition(transition) {
-      //saving session url - TODO: add a util or service - lohuynh
-      if (transition.intent.name && transition.intent.name !== 'logout') {
-        this.set('session.store.fromUrl', {lastIntentTransition: transition});
-      }
-    },
-
-    /**
-    * Refresh route's model.
-    */
-    refreshModel() {
-      this.replaceWith({ queryParams: { openReport: false } });
-    },
-
-    /**
-    * Refresh anomaly data when changes are made
-    */
-    refreshAnomalyTable() {
-      const { anomalyIds, exploreDimensions } = this.currentModel;
-      if (anomalyIds && anomalyIds.length) {
-        get(this, 'loadAnomalyData').perform(anomalyIds, exploreDimensions);
-      }
-    },
-
-    /**
-    * Change link state in parent controller to reflect transition to tuning route
-    */
-    updateParentLink() {
-      setProperties(this.controllerFor('manage.alert'), {
-        isOverViewModeActive: false,
-        isEditModeActive: true
-      });
-      // Cancel route's main concurrency tasks
-      get(this, 'loadAnomalyData').cancelAll();
-      get(this, 'loadGraphData').cancelAll();
-    },
-
-    /**
-     * Handle any errors occurring in model/afterModel in parent route
-     * https://www.emberjs.com/api/ember/2.16/classes/Route/events/error?anchor=error
-     * https://guides.emberjs.com/v2.18.0/routing/loading-and-error-substates/#toc_the-code-error-code-event
-     */
-    error() {
-      return true;
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/template.hbs
deleted file mode 100644
index 0539b1a..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/explore/template.hbs
+++ /dev/null
@@ -1,364 +0,0 @@
-<div class="manage-alert-explore">
-  {{#if (not isReplayPending)}}
-    {{!-- Date range selector --}}
-    {{range-pill-selectors
-      title="Showing"
-      maxTime=maxTime
-      uiDateFormat=uiDateFormat
-      activeRangeEnd=activeRangeEnd
-      activeRangeStart=activeRangeStart
-      timeRangeOptions=timeRangeOptions
-      timePickerIncrement=timePickerIncrement
-      selectAction=(action "onRangeSelection")
-    }}
-
-    {{#if isPageLoadFailure}}
-      {{#bs-alert type="danger" class="te-form__banner te-form__banner--failure"}}
-        <strong>Error:</strong> Failed to load performance data.
-      {{/bs-alert}}
-    {{/if}}
-
-    <div class="te-horizontal-cards te-content-block">
-      <h4 class="te-self-serve__block-title">Alert Performance</h4>
-      <p class="te-self-serve__block-subtext te-self-serve__block-subtext--narrow">All estimated performance numbers are based on reviewed anomalies.</p>
-      <a class="te-self-serve__side-link" {{action "onClickTuneSensitivity" this}}>
-        <i class="glyphicon glyphicon-cog te-icon__inline-link"></i> Customize sensitivity
-      </a>
-      <div class="te-horizontal-cards__container">
-        {{!-- Alert anomaly stats cards --}}
-        {{anomaly-stats-block
-          isTunePreviewActive=isTunePreviewActive
-          displayMode="explore"
-          anomalyStats=anomalyStats
-        }}
-      </div>
-      {{#if repRunStatus}}
-        <p class="te-self-serve__block-subtext te-self-serve__block-subtext--normal">Replay in progress. Please check back later...</p>
-      {{/if}}
-    </div>
-
-    {{#if isReportSuccess}}
-      {{#bs-alert type="success" class="te-form__banner te-form__banner--success"}}
-        <strong>Success:</strong> Anomaly reported for dates <strong>{{reportedRange}}</strong>. Reloading anomalies...
-      {{/bs-alert}}
-    {{/if}}
-
-    {{#if isReportFailure}}
-      {{#bs-alert type="danger" class="te-form__banner te-form__banner--failure"}}
-        <strong>Error:</strong> Failed to save reported anomaly.
-      {{/bs-alert}}
-    {{/if}}
-
-    {{#if (and anomaliesLoaded isAnomalyLoadError)}}
-      {{#bs-alert type="danger" class="te-form__banner te-form__banner--failure"}}
-        <strong>Warning:</strong> We are not able to load data for {{model.totalAnomalies}} anomalies.
-        Please <a class="thirdeye-link-secondary thirdeye-link-secondary--warning" target="_blank" href="{{graphMailtoLink}}">ask_thirdeye</a> for confirmation.
-      {{/bs-alert}}
-    {{/if}}
-
-    <div class="te-content-block">
-      <h4 class="te-self-serve__block-title">Anomalies over time (
-        {{#if anomaliesLoaded}}
-          {{filteredAnomalies.length}}
-          {{#if isAnomalyListFiltered}}
-            of {{totalLoadedAnomalies}}
-          {{/if}}
-        {{else}}
-          ...loading anomalies
-        {{/if}})
-      </h4>
-      <a class="te-self-serve__side-link te-self-serve__side-link--high" {{action "onClickReportAnomaly" this}}>Report missing anomaly</a>
-
-      {{!-- Dimension selector --}}
-      {{#if alertHasDimensions}}
-        <div class="te-form__select te-form__select--wider col-md-3">
-          {{#power-select
-            triggerId="select-dimension"
-            triggerClass="te-form__select"
-            options=dimensionOptions
-            searchEnabled=true
-            searchPlaceholder="Type to filter..."
-            matchTriggerWidth=true
-            matchContentWidth=true
-            selected=selectedDimension
-            onchange=(action "onSelectDimension")
-            as |dimension|
-          }}
-            {{dimension}}
-          {{/power-select}}
-        </div>
-      {{/if}}
-      {{!-- Resolution selector --}}
-      {{#if totalAnomalies}}
-        <div class="col-md-3 {{if (not alertHasDimensions) "te-form__select--left"}}">
-          {{#power-select
-            triggerId="select-resolution"
-            triggerClass="te-form__select"
-            options=resolutionOptions
-            searchEnabled=false
-            matchTriggerWidth=true
-            matchContentWidth=true
-            selected=selectedResolution
-            onchange=(action "onSelectResolution")
-            as |resolution|
-          }}
-            {{resolution}}
-          {{/power-select}}
-        </div>
-      {{/if}}
-
-      {{!-- Redirect Modal --}}
-      {{#te-modal
-        isShowingModal=true
-        headerText="Your Alert Has Been Migrated"
-        noButtons=true
-        isCancellable=false
-      }}
-        <main class="te-form alert-report-modal__redirect">
-          <legend class="te-report-title">This alert has been deprecated.</legend>
-          <div class="te-form__note">Please click the following link and update any bookmarks:
-            {{#if newId}}
-              {{#link-to "manage.explore" newId class="thirdeye-link-secondary"}}
-                Migrated Alert
-              {{/link-to}}
-            {{else}}
-              {{#link-to "manage.alerts" class="thirdeye-link-secondary"}}
-                Migrated Alert
-              {{/link-to}}
-            {{/if}}
-          </div>
-        </main>
-      {{/te-modal}}
-
-      {{!-- Missing anomaly modal --}}
-      {{#te-modal
-        cancelButtonText="Cancel"
-        submitButtonText="Report"
-        submitAction=(action "onSave")
-        cancelAction=(action "onCancel")
-        isShowingModal=openReportModal
-        headerText="Report Undetected Anomaly"
-      }}
-        {{#if renderModalContent}}
-          {{alert-report-modal
-            maxTime=maxTime
-            showTimePicker=true
-            metricData=metricData
-            uiDateFormat=uiDateFormat
-            metricName=alertData.metric
-            viewRegionEnd=viewRegionEnd
-            topDimensions=topDimensions
-            alertDimension=alertDimension
-            graphMailtoLink=graphMailtoLink
-            viewRegionStart=viewRegionStart
-            alertName=alertData.functionName
-            predefinedRanges=predefinedRanges
-            dimensionOptions=dimensionOptions
-            timePickerIncrement=timePickerIncrement
-            isDimensionFetchDone=isDimensionFetchDone
-            isMetricDataLoading=isMetricDataLoading
-            isMetricDataInvalid=isMetricDataInvalid
-            inputAction=(action "onInputMissingAnomaly")
-          }}
-        {{else}}
-          {{ember-spinner}}
-        {{/if}}
-      {{/te-modal}}
-
-      {{!-- Alert page graph --}}
-      {{self-serve-graph
-        metricData=metricData
-        isMetricSelected=true
-        componentId='alert-page'
-        topDimensions=topDimensions
-        isTopDimensionsAllowed=false
-        selectedDimension=alertDimension
-        selectedDimensions=selectedDimensions
-        graphMailtoLink=graphMailtoLink
-        isMetricDataLoading=isMetricDataLoading
-        isMetricDataInvalid=isMetricDataInvalid
-        isDimensionFetchDone=isDimensionFetchDone
-      }}
-
-      {{#if filteredAnomalies}}
-        {{!-- Baseline type selector --}}
-        {{range-pill-selectors
-          title="Baseline"
-          timeRangeOptions=baselineOptions
-          selectAction=(action "onBaselineOptionClick")
-        }}
-      {{/if}}
-
-      {{!-- Alert anomaly table --}}
-      <div class="te-block-container">
-        {{#if baselineOptionsLoading}}
-          <div class="spinner-wrapper-self-serve spinner-wrapper-self-serve--custom">{{ember-spinner}}</div>
-        {{/if}}
-        <table class="te-anomaly-table">
-          {{#if filteredAnomalies}}
-            <thead>
-              <tr class="te-anomaly-table__row te-anomaly-table__head">
-                <th class="te-anomaly-table__cell-head te-anomaly-table__cell-head--left">
-                  <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "number"}}>#
-                    <i class="te-anomaly-table__icon glyphicon {{if sortColumnNumberUp "glyphicon-menu-down" "glyphicon-menu-up"}}"></i>
-                  </a>
-                </th>
-                <th class="te-anomaly-table__cell-head te-anomaly-table__cell-head--left">
-                  <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "start"}}>
-                    Start/Duration (PDT)
-                    <i class="te-anomaly-table__icon glyphicon {{if sortColumnStartUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-                  </a>
-                </th>
-                {{#if alertHasDimensions}}
-                  <th class="te-anomaly-table__cell-head te-anomaly-table__cell-head--fixed">Dimensions</th>
-                {{/if}}
-                <th class="te-anomaly-table__cell-head">
-                  <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "score"}}>
-                    Severity Score
-                    <i class="te-anomaly-table__icon glyphicon {{if sortColumnScoreUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-                  </a>
-                </th>
-                <th class="te-anomaly-table__cell-head">
-                  <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "change"}}>
-                    {{baselineTitle}}
-                    <i class="te-anomaly-table__icon glyphicon {{if sortColumnChangeUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-                  </a>
-                </th>
-                <th class="te-anomaly-table__cell-head">
-                  <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "resolution"}}>
-                    Resolution
-                    <i class="te-anomaly-table__icon glyphicon {{if sortColumnResolutionUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-                  </a>
-                </th>
-                <th class="te-anomaly-table__cell-head"></th>
-              </tr>
-            </thead>
-          {{/if}}
-          <tbody>
-            {{#each paginatedFilteredAnomalies as |anomaly|}}
-              <tr class="te-anomaly-table__row">
-                 <td class="te-anomaly-table__cell te-anomaly-table__cell--index">{{anomaly.index}}</td>
-                 <td class="te-anomaly-table__cell">
-                  <ul class="te-anomaly-table__list te-anomaly-table__list--left">
-                    <li class="te-anomaly-table__list-item te-anomaly-table__list-item--stronger">
-                      <a target="_blank" class="te-anomaly-table__link" href="/app/#/rootcause?anomalyId={{anomaly.anomalyId}}">
-                        {{anomaly.startDateStr}}
-                      </a>
-                    </li>
-                    <li class="te-anomaly-table__list-item te-anomaly-table__list-item--lighter">{{anomaly.durationStr}}</li>
-                  </ul>
-                 </td>
-                 {{#if alertHasDimensions}}
-                   <td class="te-anomaly-table__cell">
-                    <ul class="te-anomaly-table__list">
-                     {{#each anomaly.dimensionList as |dimension|}}
-                        <li class="te-anomaly-table__list-item te-anomaly-table__list-item--smaller" title="{{dimension.dimensionVal}}">
-                          {{dimension.dimensionKey}}: <span class="stronger">{{dimension.dimensionVal}}</span>
-                        </li>
-                     {{else}}
-                        -
-                     {{/each}}
-                    </ul>
-                   </td>
-                 {{/if}}
-                 <td class="te-anomaly-table__cell">{{anomaly.severityScore}}</td>
-                 <td class="te-anomaly-table__cell">
-                  <ul class="te-anomaly-table__list">
-                    <li>{{anomaly.shownCurrent}} / {{anomaly.shownBaseline}}</li>
-                    <li class="te-anomaly-table__value-label te-anomaly-table__value-label--{{calculate-direction anomaly.shownChangeRate}}">
-                      {{#if (not anomaly.isNullChangeRate)}}
-                        ({{anomaly.shownChangeRate}}%)
-                      {{else}}
-                        (N/A)
-                      {{/if}}
-                    </li>
-                  </ul>
-                 </td>
-                 <td class="te-anomaly-table__cell">
-                    {{#if renderStatusIcon}}
-                      {{#if anomaly.showResponseSaved}}
-                        <i class="te-anomaly-table__icon--status glyphicon glyphicon-ok-circle"></i>
-                      {{/if}}
-                      {{#if anomaly.showResponseFailed}}
-                        <i class="te-anomaly-table__icon--status te-anomaly-table__icon--error glyphicon glyphicon-remove-circle"></i>
-                      {{/if}}
-                    {{/if}}
-
-                    {{#if anomaly.isUserReported}}
-                      <div class="te-anomaly-table__text te-anomaly-table__text--explore">User Reported</div>
-                      <div class="te-anomaly-table__comment">
-                        <i class="glyphicon glyphicon-th-list"></i>
-                        {{#tooltip-on-element class="te-anomaly-table__tooltip"}}
-                          {{anomaly.anomalyFeedbackComments}}
-                        {{/tooltip-on-element}}
-                      </div>
-                    {{else}}
-                      {{#power-select
-                        triggerId=anomaly.anomalyId
-                        triggerClass="te-anomaly-table__select"
-                        options=responseOptions
-                        searchEnabled=false
-                        selected=(get labelMap anomaly.anomalyFeedback)
-                        onchange=(action "onChangeAnomalyResponse" anomaly)
-                        as |response|
-                      }}
-                        {{response}}
-                      {{/power-select}}
-                    {{/if}}
-                 </td>
-                 <td class="te-anomaly-table__cell te-anomaly-table__cell--feedback">
-                    <div class="te-anomaly-table__link-wrapper">
-                      {{#link-to 'rootcause' (query-params anomalyId=anomaly.anomalyId) target="_blank" class="te-anomaly-table__link"}}
-                        Investigate
-                      {{/link-to}}
-                    </div>
-                 </td>
-              </tr>
-            {{/each}}
-          </tbody>
-        </table>
-      </div>
-
-      {{!--pagination--}}
-      {{#if (gt pagesNum 1)}}
-        <nav class="text-center" aria-label="Page navigation">
-          <ul class="pagination">
-            <li class={{if (eq currentPage 1) 'active disabled'}} >
-              <a href="#" {{action "onPaginationClick" 1}} aria-label="First">
-                <span aria-hidden="true">First</span>
-              </a>
-            </li>
-            <li class={{if (eq currentPage 1) 'active disabled'}}>
-              <a href="#" {{action "onPaginationClick" "previous"}} aria-label="Previous">
-                <span aria-hidden="true">Previous</span>
-              </a>
-            </li>
-            {{#each viewPages as |page|}}
-              <li class={{if (eq page currentPage) 'active'}}><a href="#" {{action "onPaginationClick" page}}>{{page}}</a></li>
-            {{/each}}
-            <li class={{if (eq currentPage pagesNum) 'disabled'}} >
-              <a href="#" {{action "onPaginationClick" "next"}} aria-label="Next">
-                <span aria-hidden="true">Next</span>
-              </a>
-            </li>
-            <li class={{if (eq currentPage pagesNum) 'disabled'}} >
-              <a href="#" {{action "onPaginationClick" pagesNum}} aria-label="Last">
-                <span aria-hidden="true">Last</span>
-              </a>
-            </li>
-          </ul>
-        </nav>
-      {{/if}}
-    </div>
-
-  {{else}}
-    <div class="te-alert-page-pending">
-      <img src="{{rootURL}}assets/images/te-alert-pending.png" class="te-alert-page-pending__image" alt="alert setup processing">
-      <h2 class="te-alert-page-pending__title">Setting up your alert</h2>
-      <div class="te-alert-page-pending__loader"></div>
-      <p class="te-alert-page-pending__text">
-        This may take up to a minute<br/>We will send you an email when it's done!
-      </p>
-    </div>
-  {{/if}}
-</div>
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/route.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/route.js
deleted file mode 100644
index 2182f36..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/route.js
+++ /dev/null
@@ -1,171 +0,0 @@
-/**
- * Handles the 'alert details' route.
- * @module manage/alert/route
- * @exports manage alert model
- */
-import RSVP from 'rsvp';
-import fetch from 'fetch';
-import moment from 'moment';
-import { later } from '@ember/runloop';
-import { isPresent } from "@ember/utils";
-import Route from '@ember/routing/route';
-import { inject as service } from '@ember/service';
-import config from 'thirdeye-frontend/config/environment';
-import { checkStatus, buildDateEod } from 'thirdeye-frontend/utils/utils';
-import { selfServeApiCommon } from 'thirdeye-frontend/utils/api/self-serve';
-
-// Setup for query param behavior
-const queryParamsConfig = {
-  refreshModel: true,
-  replace: false
-};
-
-export default Route.extend({
-  queryParams: {
-    jobId: queryParamsConfig
-  },
-
-  durationCache: service('services/duration'),
-  session: service(),
-
-  beforeModel(transition) {
-    const id = transition.params['manage.alert'].alert_id;
-    const { jobId, functionName } = transition.queryParams;
-    const duration = '3m';
-    const startDate = buildDateEod(3, 'month').valueOf();
-    const endDate = moment().utc().valueOf();
-
-    // Enter default 'explore' route with defaults loaded in URI
-    // An alert Id of 0 means there is an alert creation error to display
-    if (transition.targetName === 'manage.alert.index' && Number(id) !== -1) {
-      this.transitionTo('manage.alert.explore', id, { queryParams: {
-        duration,
-        startDate,
-        endDate,
-        functionName: null,
-        jobId
-      }});
-
-      // Save duration to service object for session availability
-      this.get('durationCache').setDuration({ duration, startDate, endDate });
-    }
-  },
-
-  model(params, transition) {
-    const { alert_id: id, jobId, functionName } = params;
-    if (!id) { return; }
-
-    // Fetch all the basic alert data needed in manage.alert subroutes
-    // Apply calls from go/te-ss-alert-flow-api
-    return RSVP.hash({
-      id,
-      jobId,
-      functionName: functionName || 'Unknown',
-      isLoadError: Number(id) === -1,
-      destination: transition.targetName,
-      alertData: fetch(selfServeApiCommon.alertById(id)).then(checkStatus),
-      email: fetch(selfServeApiCommon.configGroupByAlertId(id)).then(checkStatus),
-      allConfigGroups: fetch(selfServeApiCommon.allConfigGroups).then(checkStatus),
-      allAppNames: fetch(selfServeApiCommon.allApplications).then(checkStatus)
-    });
-  },
-
-  resetController(controller, isExiting) {
-    this._super(...arguments);
-    if (isExiting) {
-      controller.set('alertData', {});
-    }
-  },
-
-  setupController(controller, model) {
-    this._super(controller, model);
-
-    const {
-      id,
-      alertData,
-      pathInfo,
-      jobId,
-      isLoadError,
-      functionName,
-      destination,
-      allConfigGroups
-    } = model;
-
-    const newAlertData = !alertData ? {} : alertData;
-    let errorText = '';
-
-    // Itereate through config groups to enhance all alerts with extra properties (group name, application)
-    allConfigGroups.forEach((config) => {
-      let groupFunctionIds = config.emailConfig && config.emailConfig.functionIds ? config.emailConfig.functionIds : [];
-      let foundMatch = groupFunctionIds.find(funcId => funcId === Number(id));
-      if (foundMatch) {
-        Object.assign(newAlertData, {
-          application: config.application,
-          group: config.name
-        });
-      }
-    });
-
-    const isEditModeActive = destination.includes('edit') || destination.includes('tune');
-    const pattern = newAlertData.alertFilter ? newAlertData.alertFilter.pattern : 'N/A';
-    const granularity = newAlertData.bucketSize && newAlertData.bucketUnit ? `${newAlertData.bucketSize}_${newAlertData.bucketUnit}` : 'N/A';
-    Object.assign(newAlertData, { pattern, granularity });
-
-    // We do not have a valid alertId. Set error state.
-    if (isLoadError) {
-      Object.assign(newAlertData, { functionName, isActive: false });
-      errorText = `${functionName.toUpperCase()} has failed to create. Please try again or email ${config.email}`;
-    }
-
-    controller.setProperties({
-      id,
-      pathInfo,
-      errorText,
-      isLoadError,
-      isEditModeActive,
-      alertData: newAlertData,
-      isTransitionDone: true,
-      isReplayPending: isPresent(jobId)
-    });
-  },
-
-  actions: {
-    /**
-     * Set loader on start of transition
-     */
-    willTransition(transition) {
-      //saving session url - TODO: add a util or service - lohuynh
-      if (transition.intent.name && transition.intent.name !== 'logout') {
-        this.set('session.store.fromUrl', {lastIntentTransition: transition});
-      }
-
-      this.controller.set('isTransitionDone', false);
-      if (transition.targetName === 'manage.alert.index') {
-        this.refresh();
-      }
-    },
-
-    /**
-     * Once transition is complete, remove loader
-     */
-    didTransition() {
-      this.controller.set('isTransitionDone', true);
-      // This is needed in order to update the links in this parent route,
-      // giving the "currentRouteName" time to resolve
-      later(this, () => {
-        if (this.router.currentRouteName.includes('explore')) {
-          this.controller.set('isEditModeActive', false);
-        }
-      });
-    },
-
-    // // Sub-route errors will bubble up to this
-    error() {
-      if (this.controller) {
-        this.controller.set('isLoadError', true);
-      }
-      return true;//pass up stream
-    }
-  }
-
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/alert/template.hbs
deleted file mode 100644
index f297baf..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/template.hbs
+++ /dev/null
@@ -1,59 +0,0 @@
-<section class="te-page__top te-search-results {{if isEditModeActive "te-search-results--slim"}}">
-  <div class="container">
-
-    {{#self-serve-alert-details
-      alertData=alertData
-      isLoadError=isLoadError
-      displayMode="single"
-    }}
-
-      {{#if (not isReplayPending)}}
-        <div class="te-search-results__cta">
-          {{#if isEditModeActive}}
-            <button {{action "onClickNavToOverview"}} class="te-button te-button--outline">Back to overview</button>
-          {{else}}
-            <button class="te-button te-button--outline" {{action "onClickEdit"}}>Edit</button>
-          {{/if}}
-        </div>
-      {{/if}}
-
-    {{/self-serve-alert-details}}
-
-    {{#if isEditModeActive}}
-      <div class="te-topcard-subnav">
-        <div class="te-topcard-subnav__item">
-          <span {{action "setEditModeActive"}}>
-            {{#link-to "manage.alert.edit" alertData.id (query-params refresh=true) class="thirdeye-link thirdeye-link--smaller thirdeye-link--nav" activeClass="thirdeye-link--active"}}
-              Edit alert settings
-            {{/link-to}}
-          </span>
-        </div>
-        <div class="te-topcard-subnav__item">
-          <span {{action "setEditModeActive"}}>
-            {{#link-to "manage.alert.tune" alertData.id class="thirdeye-link thirdeye-link--smaller thirdeye-link--nav" activeClass="thirdeye-link--active"}}
-              Tune alert sensitivity
-            {{/link-to}}
-          </span>
-        </div>
-      </div>
-    {{/if}}
-
-  </div>
-</section>
-
-<section class="te-page__bottom">
-  <div class="container">
-    {{#if isLoadError}}
-      <div class="te-alert-page-pending">
-        <img src="{{rootURL}}assets/images/te-alert-error.png" class="te-alert-page-pending__image te-alert-page-pending__image--error" alt="error">
-        <h2 class="te-alert-page-pending__title">Oops, something went wrong</h2>
-        <p class="te-alert-page-pending__text">{{errorText}}</p>
-      </div>
-    {{else}}
-      {{#if (not isTransitionDone)}}
-        <div class="spinner-wrapper-self-serve spinner-wrapper-self-serve__content-block">{{ember-spinner}}</div>
-      {{/if}}
-      {{outlet}}
-    {{/if}}
-  </div>
-</section>
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/controller.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/controller.js
deleted file mode 100644
index 6cfe919..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/controller.js
+++ /dev/null
@@ -1,446 +0,0 @@
-/**
- * Controller for Alert Details Page: Tune Sensitivity Tab
- * @module manage/alert/tune
- * @exports manage/alert/tune
- */
-import _ from 'lodash';
-import moment from 'moment';
-import Controller from '@ember/controller';
-import { later } from '@ember/runloop';
-import { isPresent } from "@ember/utils";
-import { computed, set, get, getProperties, setProperties } from '@ember/object';
-import { inject as service } from '@ember/service';
-import { buildDateEod } from 'thirdeye-frontend/utils/utils';
-import { anomalyResponseMap } from 'thirdeye-frontend/utils/anomaly';
-import { buildAnomalyStats } from 'thirdeye-frontend/utils/manage-alert-utils';
-
-export default Controller.extend({
-  /**
-   * Be ready to receive time span for anomalies via query params
-   */
-  queryParams: ['duration', 'startDate', 'endDate'],
-  duration: null,
-  startDate: null,
-  endDate: null,
-
-  /**
-   * Make duration service accessible
-   */
-  durationCache: service('services/duration'),
-
-  /**
-   * Make toast service accessible
-   */
-  notifications: service('toast'),
-
-  /**
-   * Set initial view values
-   * @method initialize
-   * @return {undefined}
-   */
-  initialize() {
-    this.setProperties({
-      filterBy: 'All',
-      isGraphReady: false,
-      isTunePreviewActive: false,
-      isTuneSaveSuccess: false,
-      isTuneSaveFailure: false,
-      selectedTuneType: 'current',
-      predefinedRanges: {},
-      today: moment(),
-      selectedSortMode: '',
-      activeRangeStart: '',
-      activeRangeEnd: '',
-      removedAnomalies: 0,
-      sortColumnStartUp: false,
-      sortColumnScoreUp: false,
-      sortColumnChangeUp: false,
-      isAnomalyTableLoading: false,
-      sortColumnResolutionUp: false,
-      isPerformanceDataLoading: false,
-      customMttdClasses: 'form-control te-input'
-    });
-  },
-
-  /**
-   * Severity power-select options
-   * @type {Array}
-   */
-  tuneSeverityOptions: computed('severityMap', function() {
-    return Object.keys(this.get('severityMap'));
-  }),
-
-  /**
-   * Returns selectable pattern options for power-select
-   * @type {Array}
-   */
-  tunePatternOptions: computed('patternMap', function() {
-    return Object.keys(this.get('patternMap'));
-  }),
-
-  /**
-   * Mapping anomaly table column names to corresponding prop keys
-   */
-  sortMap: {
-    start: 'anomalyStart',
-    score: 'severityScore',
-    change: 'changeRate',
-    resolution: 'anomalyFeedback'
-  },
-
-  /**
-   * Conditional formatting for tuning fields
-   * @type {Boolean}
-   */
-  isTuneAmountPercent: computed('selectedSeverityOption', function() {
-    return this.get('selectedSeverityOption') !== 'Absolute Value of Change';
-  }),
-
-  /**
-   * Builds the new autotune filter from custom tuning options
-   * @type {String}
-   */
-  customTuneQueryString: computed(
-    'selectedSeverityOption',
-    'customPercentChange',
-    'customMttdChange',
-    'selectedTunePattern',
-    function() {
-      const {
-        severityMap,
-        patternMap,
-        customPercentChange: amountChange,
-        selectedTunePattern: selectedPattern,
-        selectedSeverityOption: selectedSeverity
-      } = this.getProperties('severityMap', 'patternMap', 'customPercentChange', 'selectedTunePattern', 'selectedSeverityOption');
-      const isPercent = selectedSeverity === 'Percentage of Change';
-      const mttdVal = Number(this.get('customMttdChange')).toFixed(2);
-      const severityThresholdVal = isPercent ? (Number(amountChange)/100).toFixed(2) : amountChange;
-      const featureString = `window_size_in_hour,${severityMap[selectedSeverity]}`;
-      const mttdString = `window_size_in_hour=${mttdVal};${severityMap[selectedSeverity]}=${severityThresholdVal}`;
-      const patternString = patternMap[selectedPattern] ? `&pattern=${encodeURIComponent(patternMap[selectedPattern])}` : '';
-      const configString = `&tuningFeatures=${encodeURIComponent(featureString)}&mttd=${encodeURIComponent(mttdString)}${patternString}`;
-      return { configString, severityVal: severityThresholdVal };
-    }
-  ),
-
-  /**
-   * Indicates the allowed date range picker increment based on granularity
-   * @type {Number}
-   */
-  timePickerIncrement: computed('alertData.windowUnit', function() {
-    const granularity = this.get('alertData.windowUnit').toLowerCase();
-
-    switch(granularity) {
-      case 'days':
-        return 1440;
-      case 'hours':
-        return 60;
-      default:
-        return 5;
-    }
-  }),
-
-  /**
-   * Allows us to enable/disable the custom tuning options
-   * @type {Boolean}
-   */
-  isCustomFieldsDisabled: computed('selectedTuneType', function() {
-    return this.get('selectedTuneType') === 'current';
-  }),
-
-  /**
-   * date-time-picker: indicates the date format to be used based on granularity
-   * @type {String}
-   */
-  uiDateFormat: computed('alertData.windowUnit', function() {
-    const granularity = this.get('alertData.windowUnit').toLowerCase();
-
-    switch(granularity) {
-      case 'days':
-        return 'MMM D, YYYY';
-      case 'hours':
-        return 'MMM D, YYYY h a';
-      default:
-        return 'MMM D, YYYY hh:mm a';
-    }
-  }),
-
-  /**
-   * Data needed to render the stats 'cards' above the anomaly graph for this alert
-   * NOTE: buildAnomalyStats util currently requires both 'current' and 'projected' props to be present.
-   * @type {Object}
-   */
-  anomalyStats: computed(
-    'alertEvalMetrics',
-    'isTuneAmountPercent',
-    'customPercentChange',
-    'alertEvalMetrics.projected',
-    function() {
-      const {
-        isTuneAmountPercent,
-        alertEvalMetrics: metrics,
-        customPercentChange: severity
-      } = this.getProperties(
-        'isTuneAmountPercent',
-        'alertEvalMetrics',
-        'customPercentChange'
-      );
-      const severityUnit = isTuneAmountPercent ? '%' : '';
-      const isPerfDataReady = _.has(metrics, 'current');
-      const statsCards = [
-        {
-          title: 'Estimated number of anomalies',
-          key: 'totalAlerts',
-          tooltip: false,
-          text: 'Estimated number of anomalies  based on alert settings'
-        },
-        {
-          title: 'Estimated precision',
-          key: 'precision',
-          units: '%',
-          tooltip: false,
-          text: 'Among all anomalies sent by the alert, the % of them that are true.'
-        },
-        {
-          title: 'Estimated recall',
-          key: 'recall',
-          units: '%',
-          tooltip: false,
-          text: 'Among all anomalies that happened, the % of them sent by the alert.'
-        },
-        {
-          title: `MTTD for > ${severity}${severityUnit} change`,
-          key: 'mttd',
-          units: 'hrs',
-          tooltip: false,
-          text: `Minimum time to detect for anomalies with > ${severity}${severityUnit} change`
-        }
-      ];
-
-      return isPerfDataReady ? buildAnomalyStats(metrics, statsCards, false) : [];
-    }
-  ),
-
-  /**
-   * Data needed to render the stats 'cards' above the anomaly graph for this alert
-   * @type {Object}
-   */
-  diffedAnomalies: computed(
-    'anomalyData',
-    'filterBy',
-    'selectedSortMode',
-    function() {
-      const {
-        anomalyData: anomalies,
-        filterBy: activeFilter,
-        selectedSortMode
-      } = this.getProperties('anomalyData', 'filterBy', 'selectedSortMode');
-      let filterKey = '';
-      let filteredAnomalies = anomalies || [];
-      let num = 1;
-
-      switch (activeFilter) {
-        case 'True Anomalies':
-          filterKey = 'True Anomaly';
-          break;
-        case 'False Alarms':
-          filterKey = 'False Alarm';
-          break;
-        case 'User Reported':
-          filterKey = 'New Trend';
-          break;
-        default:
-          filterKey = '';
-      }
-
-      // Filter anomalies in table according to filterkey
-      if (activeFilter !== 'All') {
-        filteredAnomalies = anomalies.filter(anomaly => anomaly.anomalyFeedback === filterKey);
-      }
-      if (selectedSortMode) {
-        let [ sortKey, sortDir ] = selectedSortMode.split(':');
-        if (sortDir === 'up') {
-          filteredAnomalies = filteredAnomalies.sortBy(this.get('sortMap')[sortKey]);
-        } else {
-          filteredAnomalies = filteredAnomalies.sortBy(this.get('sortMap')[sortKey]).reverse();
-        }
-      }
-
-      // Number the list
-      filteredAnomalies.forEach((anomaly) => {
-        set(anomaly, 'index', num);
-        setProperties(anomaly, {
-          index: num,
-          feedbackLabel: anomalyResponseMap[anomaly.anomalyFeedback] || anomaly.anomalyFeedback
-        });
-        num++;
-      });
-
-      return filteredAnomalies;
-    }
-  ),
-
-  /**
-   * Reset the controller values on exit
-   * @method clearAll
-   */
-  clearAll() {
-    this.setProperties({
-      alertEvalMetrics: {}
-    });
-  },
-
-  actions: {
-
-    /**
-     * This field will not accept empty input - default back to the original value
-     * @method onChangeSeverityValue
-     * @param {String} severity - the custom tuning severity input
-     */
-    onChangeSeverityValue(severity) {
-      if (!isPresent(severity)) {
-        this.set('customPercentChange', this.model.customPercentChange);
-      }
-    },
-
-    /**
-     * This field will not accept empty input - default back to the original value
-     * @method onChangeMttdValue
-     * @param {String} mttd - the custom tuning mttd input
-     */
-    onChangeMttdValue(mttd) {
-      if (!isPresent(mttd)) {
-        this.set('customMttdChange', this.model.customMttdChange);
-      }
-    },
-
-    /**
-     * Sets the new custom date range for anomaly coverage
-     * @method onRangeSelection
-     * @param {Object} rangeOption - the user-selected time range to load
-     */
-    onRangeSelection(rangeOption) {
-      const {
-        start,
-        end,
-        value: duration
-      } = rangeOption;
-      const durationObj = {
-        duration,
-        startDate: moment(start).valueOf(),
-        endDate: moment(end).valueOf()
-      };
-      // Cache the new time range and update page with it
-      this.get('durationCache').setDuration(durationObj);
-      this.transitionToRoute({ queryParams: durationObj });
-    },
-
-    /**
-     * Save the currently loaded tuning options
-     */
-    onSubmitTuning() {
-      this.send('submitTuningRequest', this.get('autoTuneId'));
-    },
-
-    /**
-     * Handle "reset" click - reload the model
-     */
-    onResetPage() {
-      this.initialize();
-      this.set('alertEvalMetrics.projected', this.get('originalProjectedMetrics'));
-      this.send('resetTuningParams', this.get('alertData'));
-    },
-
-    /**
-     * Replaces the 'tableStats' object with a new one with selected filter
-     * activated and triggers table filtering.
-     * @param {String} metric - label of the currently selected category
-     */
-    toggleCategory(metric) {
-      const stats = this.get('tableStats');
-      const newStats = stats.map((cat) => {
-        return {
-          count: cat.count,
-          label: cat.label,
-          isActive: false
-        };
-      });
-      // Activate selected metric in our new stats object
-      newStats.find(cat => cat.label === metric).isActive = true;
-      // Apply new table stats object and trigger re-render of filtered anomalies
-      this.setProperties({
-        tableStats: newStats,
-        filterBy: metric
-      });
-    },
-
-    /**
-     * Handle sorting for each sortable table column
-     * @param {String} sortKey  - stringified start date
-     */
-    toggleSortDirection(sortKey) {
-      const propName = `sortColumn${sortKey.capitalize()}Up` || '';
-
-      this.toggleProperty(propName);
-      if (this.get(propName)) {
-        this.set('selectedSortMode', `${sortKey}:up`);
-      } else {
-        this.set('selectedSortMode', `${sortKey}:down`);
-      }
-      // On sort, set table to first pagination page
-      this.set('currentPage', 1);
-    },
-
-    /**
-     * On "preview" click, display the resulting anomaly table and trigger
-     * tuning if we have custom settings (tuning data for default option is already loaded)
-     */
-    onClickPreviewPerformance() {
-      const defaultConfig = { configString: '' };
-      const { customMttdClasses, mttdMinimums, alertData, notifications, customMttdChange } = getProperties(this,
-        'customMttdClasses',
-        'mttdMinimums',
-        'alertData',
-        'notifications',
-        'customMttdChange'
-      );
-      const granularityBucket = alertData.bucketUnit ? alertData.bucketUnit.toLowerCase() : null;
-      const isBucketDefaultPresent = granularityBucket && mttdMinimums.hasOwnProperty(granularityBucket);
-      const isMttdSetTooLow = isBucketDefaultPresent ? Number(customMttdChange) < Number(mttdMinimums[granularityBucket]) : false;
-      const currentMinimumMttd = mttdMinimums[granularityBucket];
-      const mttdUnit = granularityBucket === 'hours' ? 'hour' : 'hours';
-      const mttdErrMsg = `MTTD is set too low for metric granularity. Please enter a value of at least ${currentMinimumMttd} ${mttdUnit}.`;
-      const toastOptions = {
-        timeOut: '10000',
-        positionClass: 'toast-top-right'
-      };
-
-      // Check if MTTD is set below minimums and display error message
-      if (isMttdSetTooLow) {
-        set(this, 'customMttdClasses', `${customMttdClasses} te-input--error`);
-        notifications.error(mttdErrMsg, 'MTTD range error', toastOptions);
-        document.querySelector('#custom-tune-mttd').select();
-        return;
-      } else {
-        notifications.clear();
-        setProperties(this, {
-          customMttdClasses: 'form-control te-input',
-          isPerformanceDataLoading: true
-        });
-      }
-
-      if (this.get('selectedTuneType') === 'custom') {
-        // Trigger preview with custom params
-        this.send('triggerTuningSequence', this.get('customTuneQueryString'));
-      } else {
-        // When user wants to preview using "current" settings, our request does not contain custom params.
-        this.send('triggerTuningSequence', defaultConfig);
-      }
-
-      // Reset table filter
-      set(this, 'filterBy', 'All');
-    }
-  }
-
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/route.js b/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/route.js
deleted file mode 100644
index 63380c1..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/route.js
+++ /dev/null
@@ -1,487 +0,0 @@
-/**
- * Handles the 'explore' route for manage alert
- * @module manage/alert/edit/explore
- * @exports manage/alert/edit/explore
- */
-import RSVP from "rsvp";
-import _ from 'lodash';
-import fetch from 'fetch';
-import moment from 'moment';
-import Route from '@ember/routing/route';
-import { isPresent } from "@ember/utils";
-import { later } from "@ember/runloop";
-import { task } from 'ember-concurrency';
-import {
-  checkStatus,
-  postProps,
-  buildDateEod,
-  toIso
-} from 'thirdeye-frontend/utils/utils';
-import {
-  enhanceAnomalies,
-  setUpTimeRangeOptions,
-  toIdGroups,
-  extractSeverity
-} from 'thirdeye-frontend/utils/manage-alert-utils';
-import { inject as service } from '@ember/service';
-
-/**
- * Basic alert page defaults
- */
-const durationDefault = '3m';
-const defaultSeverity = '0.3';
-const dateFormat = 'YYYY-MM-DD';
-const displayDateFormat = 'YYYY-MM-DD HH:mm';
-const defaultDurationObj = {
-  duration: '3m',
-  startDate: buildDateEod(3, 'month').valueOf(),
-  endDate: moment().valueOf()
-};
-
-/**
- * Pattern display options (power-select) and values
- */
-const patternMap = {
-  'Up and Down': 'UP,DOWN',
-  'Up Only': 'UP',
-  'Down Only': 'DOWN'
-};
-
-/**
- * Severity display options (power-select) and values
- */
-const severityMap = {
-  'Percentage of Change': 'weight',
-  'Absolute Value of Change': 'deviation'
-};
-
-/**
- * If no filter data is set for sensitivity, use this
- */
-const sensitivityDefaults = {
-  defaultMttdVal: '5',
-  selectedSeverityOption: 'Percentage of Change',
-  selectedTunePattern: 'Up and Down',
-  defaultPercentChange: '0.3',
-  // Set granularity minimums in number of hours
-  mttdGranularityMinimums: {
-    days: 24,
-    hours: 1,
-    minutes: 0.25
-  }
-};
-
-/**
- * Build the object to populate anomaly table feedback categories
- * @param {Array} anomalies - list of all deduped and filtered anomalies
- * @returns {Object}
- */
-const anomalyTableStats = (anomalies) => {
-  const trueAnomalies = anomalies ? anomalies.filter(anomaly => anomaly.anomalyFeedback === 'True anomaly') : 0;
-  const falseAnomalies = anomalies ? anomalies.filter(anomaly => anomaly.anomalyFeedback === 'False Alarm') : 0;
-  const userAnomalies = anomalies ? anomalies.filter(anomaly => anomaly.anomalyFeedback === 'Confirmed - New Trend') : 0;
-
-  return [
-    {
-      count: anomalies.length,
-      label: 'All',
-      isActive: true
-    },
-    {
-      count: trueAnomalies.length,
-      label: 'True Anomalies',
-      isActive: false
-    },
-    {
-      count: falseAnomalies.length,
-      label: 'False Alarms',
-      isActive: false
-    },
-    {
-      count: userAnomalies.length,
-      label: 'User Created',
-      isActive: false
-    }
-  ];
-};
-
-/**
- * Set up select & input field defaults for sensitivity settings
- * @param {Array} alertData - properties for the currently loaded alert
- * @returns {Object}
- */
-const processDefaultTuningParams = (alertData) => {
-  let {
-    defaultMttdVal,
-    selectedSeverityOption,
-    selectedTunePattern,
-    defaultPercentChange,
-    mttdGranularityMinimums
-  } = sensitivityDefaults;
-
-  // Cautiously derive tuning data from alert filter properties
-  const featureString = 'window_size_in_hour';
-  const alertFilterObj = alertData.alertFilter || null;
-  const alertPattern = alertFilterObj ? alertFilterObj.pattern : null;
-  const isFeaturesPropFormatted = _.has(alertFilterObj, 'features') && alertFilterObj.features.includes(featureString);
-  const isMttdPropFormatted =  _.has(alertFilterObj, 'mttd') && alertFilterObj.mttd.includes(`${featureString}=`);
-  const alertFeatures = isFeaturesPropFormatted ? alertFilterObj.features.split(',')[1] : null;
-  const alertMttd = isMttdPropFormatted ? alertFilterObj.mttd.split(';') : null;
-  const granularityBucket = alertData.bucketUnit ? alertData.bucketUnit.toLowerCase() : null;
-  const isBucketDefaultPresent = granularityBucket && mttdGranularityMinimums.hasOwnProperty(granularityBucket);
-  const defaultMttdChange = isBucketDefaultPresent ? mttdGranularityMinimums[granularityBucket] : defaultMttdVal;
-
-  // Load saved pattern into pattern options
-  const savedTunePattern = alertPattern ? alertPattern : 'UP,DOWN';
-  for (var patternKey in patternMap) {
-    if (savedTunePattern === patternMap[patternKey]) {
-      selectedTunePattern = patternKey;
-    }
-  }
-
-  // TODO: enable once issue resolved in backend (not saving selection to new feature string)
-  const savedSeverityPattern = alertMttd ? alertMttd[1].split('=')[0] : 'weight';
-  const isAbsValue = savedSeverityPattern === 'deviation';
-  for (var severityKey in severityMap) {
-    if (savedSeverityPattern === severityMap[severityKey]) {
-      selectedSeverityOption = severityKey;
-    }
-  }
-
-  // Load saved mttd
-  const mttdValue = alertMttd ? alertMttd[0].split('=')[1] : 'N/A';
-  const customMttdChange = !isNaN(mttdValue) ? Math.round(Number(mttdValue)) : defaultMttdChange;
-
-  // Load saved severity value
-  const severityValue = alertMttd ? alertMttd[1].split('=')[1] : 'N/A';
-  const rawPercentChange = !isNaN(severityValue) ? Number(severityValue) : defaultPercentChange;
-  const customPercentChange = isAbsValue ? rawPercentChange : rawPercentChange * 100;
-
-  return { selectedSeverityOption, selectedTunePattern, customPercentChange, customMttdChange };
-};
-
-/**
- * Fetches all anomaly data for found anomalies - downloads all 'pages' of data from server
- * in order to handle sorting/filtering on the entire set locally. Start/end date are not used here.
- * @param {Array} anomalyIds - list of all found anomaly ids
- * @returns {RSVP promise}
- */
-const fetchCombinedAnomalies = (anomalyIds) => {
-  if (anomalyIds.length) {
-    const idGroups = toIdGroups(anomalyIds);
-    const anomalyPromiseHash = idGroups.map((group, index) => {
-      let idStringParams = `anomalyIds=${encodeURIComponent(idGroups[index].toString())}`;
-      let getAnomalies = fetch(`/anomalies/search/anomalyIds/0/0/${index + 1}?${idStringParams}`).then(checkStatus);
-      return RSVP.resolve(getAnomalies);
-    });
-    return RSVP.all(anomalyPromiseHash);
-  } else {
-    return RSVP.resolve([]);
-  }
-};
-
-/**
- * Fetches severity scores for all anomalies
- * TODO: Move this and other shared requests to a common service
- * @param {Array} anomalyIds - list of all found anomaly ids
- * @returns {RSVP promise}
- */
-const fetchSeverityScores = (anomalyIds) => {
-  if (anomalyIds.length) {
-    const anomalyPromiseHash = anomalyIds.map((id) => {
-      return RSVP.hash({
-        id,
-        score: fetch(`/dashboard/anomalies/score/${id}`).then(checkStatus)
-      });
-    });
-    return RSVP.allSettled(anomalyPromiseHash);
-  } else {
-    return RSVP.resolve([]);
-  }
-};
-
-/**
- * Returns a promise hash to fetch to fetch fresh projected anomaly data after tuning adjustments
- * @param {Date} startDate - start of date range
- * @param {Date} endDate - end of date range
- * @param {Sting} tuneId - current autotune filter Id
- * @param {String} alertId - current alert Id
- * @returns {Object} containing fetch promises
- */
-const tuningPromiseHash = (startDate, endDate, tuneId, alertId, severity = defaultSeverity) => {
-  const baseStart = moment(Number(startDate));
-  const baseEnd = moment(Number(endDate));
-  const tuneParams = `start=${toIso(startDate)}&end=${toIso(endDate)}`;
-  const qsParams = `start=${baseStart.utc().format(dateFormat)}&end=${baseEnd.utc().format(dateFormat)}&useNotified=true`;
-  const projectedUrl = `/detection-job/eval/autotune/${tuneId}?${tuneParams}`;
-  const projectedMttdUrl = `/detection-job/eval/projected/mttd/${tuneId}?severity=${severity}`;
-  const anomaliesUrlA = `/dashboard/anomaly-function/${alertId}/anomalies?${qsParams}`;
-  const anomaliesUrlB =`/detection-job/eval/projected/anomalies/${tuneId}?${qsParams}`;
-
-  return {
-    projectedMttd: fetch(projectedMttdUrl).then(checkStatus),
-    projectedEval: fetch(projectedUrl).then(checkStatus),
-    idListA: fetch(anomaliesUrlA).then(checkStatus),
-    idListB: fetch(anomaliesUrlB).then(checkStatus)
-  };
-};
-
-/**
- * Returns a bi-directional diff given "before" and "after" tuning anomaly Ids
- * @param {Array} listA - list of all anomaly ids BEFORE tuning
- * @param {Array} listB - list of all anomaly ids AFTER tuning
- * @returns {Object}
- */
-const anomalyDiff = (listA, listB) => {
-  return {
-    idsRemoved: listA.filter(id => !listB.includes(id)),
-    idsAdded: listB.filter(id => !listA.includes(id))
-  };
-};
-
-/**
- * Setup for query param behavior
- */
-const queryParamsConfig = {
-  refreshModel: true,
-  replace: true
-};
-
-export default Route.extend({
-  queryParams: {
-    duration: queryParamsConfig,
-    startDate: queryParamsConfig,
-    endDate: queryParamsConfig
-  },
-
-  /**
-   * Make duration service accessible
-   */
-  durationCache: service('services/duration'),
-  session: service(),
-
-  beforeModel(transition) {
-    const { duration, startDate } = transition.queryParams;
-
-    // Default to 3 months of anomalies to show if no dates present in query params
-    if (!duration || (duration !== 'custom' && duration !== '3m') || !startDate) {
-      this.transitionTo({ queryParams: defaultDurationObj });
-    }
-  },
-
-  model(params, transition) {
-    const { id, alertData } = this.modelFor('manage.alert');
-    if (!id) { return; }
-
-    // Get duration data
-    const {
-      duration,
-      startDate,
-      endDate
-    } = this.get('durationCache').getDuration(transition.queryParams, defaultDurationObj);
-
-    // Prepare endpoints for the initial eval, mttd, projected metrics calls
-    const tuneParams = `start=${toIso(startDate)}&end=${toIso(endDate)}`;
-    const tuneIdUrl = `/detection-job/autotune/filter/${id}?${tuneParams}`;
-    const evalUrl = `/detection-job/eval/filter/${id}?${tuneParams}&isProjected=TRUE`;
-    const mttdUrl = `/detection-job/eval/mttd/${id}?severity=${extractSeverity(alertData, defaultSeverity)}`;
-    const initialPromiseHash = {
-      current: fetch(evalUrl).then(checkStatus),
-      mttd: fetch(mttdUrl).then(checkStatus)
-    };
-
-    return RSVP.hash(initialPromiseHash)
-      .then((alertEvalMetrics) => {
-        Object.assign(alertEvalMetrics.current, { mttd: alertEvalMetrics.mttd});
-        return {
-          id,
-          alertData,
-          duration,
-          tuneIdUrl,
-          startDate,
-          endDate,
-          tuneParams,
-          alertEvalMetrics
-        };
-      })
-      .catch((error) => {
-        return RSVP.reject({ error, location: `${this.routeName}:model`, calls: initialPromiseHash });
-      });
-  },
-
-  setupController(controller, model) {
-    this._super(controller, model);
-
-    const {
-      id,
-      alertData,
-      duration,
-      loadError,
-      startDate,
-      endDate,
-      alertEvalMetrics
-    } = model;
-
-    // Conditionally add select option for severity
-    if (alertData.toCalculateGlobalMetric) {
-      severityMap['Site Wide Impact'] = 'site_wide_impact';
-    }
-
-    // Prepare sensitivity default values to populate tuning options from alert data
-    const {
-      selectedSeverityOption,
-      selectedTunePattern,
-      customPercentChange,
-      customMttdChange
-    } = processDefaultTuningParams(alertData);
-    Object.assign(model, { customPercentChange, customMttdChange });
-
-    controller.setProperties({
-      alertData,
-      loadError,
-      patternMap,
-      severityMap,
-      alertId: id,
-      autoTuneId: '',
-      customMttdChange,
-      customPercentChange,
-      alertEvalMetrics,
-      selectedTunePattern,
-      selectedSeverityOption,
-      mttdMinimums: sensitivityDefaults.mttdGranularityMinimums,
-      alertHasDimensions: isPresent(alertData.exploreDimensions),
-      timeRangeOptions: setUpTimeRangeOptions([durationDefault], duration)
-    });
-    controller.initialize();
-
-    // Ensure date range picker gets populated correctly
-    later(this, () => {
-      controller.setProperties({
-        activeRangeStart: moment(Number(startDate)).format(displayDateFormat),
-        activeRangeEnd: moment(Number(endDate)).format(displayDateFormat)
-      });
-    });
-  },
-
-  resetController(controller, isExiting) {
-    this._super(...arguments);
-
-    if (isExiting) {
-      this.get('triggerTuningSequence').cancelAll();
-      controller.clearAll();
-    }
-  },
-
-  saveAutoTuneSettings(id) {
-    return fetch(`/detection-job/update/filter/${id}`, postProps('')).then(checkStatus);
-  },
-
-  /**
-   * This concurrency task fetches anomaly performance metrics and data for all anomalies which
-   * would not be included in the notification set under the user-selected tuning settings.
-   * @method triggerTuningSequence
-   * @param {Object} configObj - the user-selected type and value of tuning severity thresholds
-   * @return {undefined}
-   */
-  triggerTuningSequence: task(function * (configObj) {
-    const { configString, severityVal} = configObj;
-    const {
-      id: alertId,
-      startDate,
-      endDate,
-      tuneIdUrl
-    } = this.currentModel;
-    try {
-      // Send the new tuning settings to backend to get an auto-tune Id
-      const tuneId = yield fetch(tuneIdUrl + configString, postProps('')).then(checkStatus);
-      // Use the autotune Id to fetch new performance metrics for this alert, and load them into the template
-      const performanceData = yield RSVP.hash(tuningPromiseHash(startDate, endDate, tuneId[0], alertId, severityVal));
-      const idsRemoved = anomalyDiff(performanceData.idListA, performanceData.idListB).idsRemoved;
-      const projectedStats = performanceData.projectedEval;
-      Object.assign(projectedStats, { mttd: performanceData.projectedMttd });
-      this.controller.set('alertEvalMetrics.projected', projectedStats);
-      this.controller.setProperties({
-        removedAnomalies: idsRemoved.length,
-        isTunePreviewActive: true,
-        isAnomalyTableLoading: true,
-        isPerformanceDataLoading: false
-      });
-      // Fetch all anomaly data for the list of removed anomalies
-      const rawAnomalyData = yield fetchCombinedAnomalies(idsRemoved);
-      // Fetch severity scores for each anomaly
-      const severityScores = yield fetchSeverityScores(idsRemoved);
-      const anomalyData = enhanceAnomalies(rawAnomalyData, severityScores);
-      this.controller.setProperties({
-        anomalyData,
-        autoTuneId: tuneId[0],
-        isAnomalyTableLoading: false,
-        tableStats: anomalyTableStats(anomalyData)
-      });
-    } catch(error) {
-      this.controller.setProperties({
-        loadError: true,
-        loadErrorMsg: error
-      });
-    }
-  }).cancelOn('deactivate').restartable(),
-
-  actions: {
-    /**
-     * save session url for transition on login
-     * @method willTransition
-     */
-    willTransition(transition) {
-      //saving session url - TODO: add a util or service - lohuynh
-      if (transition.intent.name && transition.intent.name !== 'logout') {
-        this.set('session.store.fromUrl', {lastIntentTransition: transition});
-      }
-    },
-
-    /**
-     * Handle any errors occurring in model/afterModel in parent route
-     * https://www.emberjs.com/api/ember/2.16/classes/Route/events/error?anchor=error
-     * https://guides.emberjs.com/v2.18.0/routing/loading-and-error-substates/#toc_the-code-error-code-event
-     */
-    error() {
-      return true;
-    },
-
-    // User clicks reset button
-    resetPage() {
-      this.transitionTo({ queryParams: defaultDurationObj });
-    },
-
-    // User resets settings
-    resetTuningParams(alertData) {
-      const {
-        selectedSeverityOption,
-        selectedTunePattern,
-        customPercentChange,
-        customMttdChange
-      } = processDefaultTuningParams(alertData);
-      this.controller.setProperties({
-        selectedSeverityOption,
-        selectedTunePattern,
-        customPercentChange,
-        customMttdChange
-      });
-    },
-
-    // User clicks "save" on previewed tune settings
-    submitTuningRequest(tuneId) {
-      this.saveAutoTuneSettings(tuneId)
-        .then((result) => {
-          this.controller.set('isTuneSaveSuccess', true);
-        })
-        .catch((error) => {
-          this.controller.set('isTuneSaveFailure', true);
-          this.controller.set('failureMessage', error);
-        });
-    },
-
-    // User clicks "preview", having configured performance settings
-    triggerTuningSequence(configObj) {
-      this.get('triggerTuningSequence').perform(configObj);
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/template.hbs
deleted file mode 100644
index 345c00e..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alert/tune/template.hbs
+++ /dev/null
@@ -1,257 +0,0 @@
-<div class="manage-alert-tune">
-  {{!-- Date range selector --}}
-  {{range-pill-selectors
-    title="Showing"
-    maxTime=maxTime
-    uiDateFormat=uiDateFormat
-    activeRangeEnd=activeRangeEnd
-    activeRangeStart=activeRangeStart
-    timeRangeOptions=timeRangeOptions
-    timePickerIncrement=timePickerIncrement
-    selectAction=(action "onRangeSelection")
-  }}
-
-  <div class="te-content-block">
-    <fieldset class="te-form__section te-form__section--first te-form__section--slim row">
-      <div class="col-xs-12">
-        <h4 class="te-self-serve__block-title">Tune alert notifications</h4>
-        <p class="te-self-serve__block-subtext">All configurations and outputs will be based on reviewed anomalies.</p>
-      </div>
-      <ul class="te-form__list col-xs-12 col-sm-8">
-        <li class="te-form__list-item">
-          {{#radio-button value="current" groupValue=selectedTuneType}}
-            <div class="te-form__list-label">Automatically tune alert settings</div>
-          {{/radio-button}}
-        </li>
-        <li class="te-form__list-item">
-          {{#radio-button value="custom" groupValue=selectedTuneType}}
-            <div class="te-form__list-label">Customize alert settings</div>
-          {{/radio-button}}
-         </li>
-      </ul>
-
-      <div class="te-form__indent-block col-xs-8">
-        <label for="alert-tune-pattern" class="te-form__list-label">Anomaly Pattern</label>
-        <div class="te-form__tune-field">
-          {{#power-select
-            triggerId="alert-tune-pattern"
-            triggerClass="te-form__tune-field"
-            verticalPosition="below"
-            renderInPlace=true
-            options=tunePatternOptions
-            searchEnabled=false
-            selected=selectedTunePattern
-            placeHolder="Select Pattern"
-            onchange=(action (mut selectedTunePattern))
-            disabled=isCustomFieldsDisabled
-            as |pattern|
-          }}
-            {{pattern}}
-          {{/power-select}}
-        </div>
-      </div>
-
-      <div class="te-form__indent-block col-xs-10">
-        <label for="alert-tune-pattern" class="te-form__list-label">When</label>
-        <div class="te-form__tune-field">
-          {{#power-select
-            triggerId="alert-tune-severity"
-            triggerClass="te-form__tune-field"
-            verticalPosition="below"
-            renderInPlace=true
-            options=tuneSeverityOptions
-            searchEnabled=false
-            selected=selectedSeverityOption
-            onchange=(action (mut selectedSeverityOption))
-            disabled=isCustomFieldsDisabled
-            as |severity|
-          }}
-            {{severity}}
-          {{/power-select}}
-        </div>
-
-        <label for="custom-tune-percent" class="te-form__list-label">is >=</label>
-        <div class="te-form__tune-field te-form__tune-field--amount">
-          {{input
-            type="text"
-            id="custom-tune-percent"
-            class="form-control te-input te-input"
-            focus-out="onChangeSeverityValue"
-            value=customPercentChange
-            disabled=isCustomFieldsDisabled
-          }}
-        </div>
-        {{#if isTuneAmountPercent}}
-          <span class="te-form__list-label--suffix">%, </span>
-        {{/if}}
-
-        <label for="custom-tune-mttd" class="te-form__list-label">MTTD should be no more than</label>
-        <div class="te-form__tune-field te-form__tune-field--amount">
-          {{input
-            type="text"
-            id="custom-tune-mttd"
-            class=customMttdClasses
-            value=customMttdChange
-            focus-out="onChangeMttdValue"
-            disabled=isCustomFieldsDisabled
-          }}
-        </div>
-        <span class="te-form__list-label">hours</span>
-      </div>
-
-      <div class="te-form__cta-row">
-        <a class="te-button te-button--link" {{action "onResetPage" preventDefault=false}}>Reset</a>
-        {{bs-button
-          defaultText="Preview performance"
-          type="outline-primary"
-          onClick=(action "onClickPreviewPerformance")
-          class="te-button te-button--outline"
-        }}
-      </div>
-    </fieldset>
-  </div>
-
-  <div class="te-horizontal-cards te-content-block">
-    <h4 class="te-self-serve__block-title">{{if isTunePreviewActive "Compare" "Tuned"}} Alert Performance</h4>
-    <div class="te-horizontal-cards__container">
-      {{#if isPerformanceDataLoading}}
-        <div class="spinner-wrapper-self-serve spinner-wrapper-self-serve__content-block">{{ember-spinner}}</div>
-      {{/if}}
-      {{!-- Alert anomaly stats cards --}}
-      {{anomaly-stats-block
-        isTunePreviewActive=isTunePreviewActive
-        displayMode="tune"
-        anomalyStats=anomalyStats
-      }}
-    </div>
-    {{#if isTunePreviewActive}}
-      <div class="te-form__cta-button">
-        {{bs-button
-          defaultText="Save tuning"
-          type="primary"
-          onClick=(action "onSubmitTuning")
-          buttonType="submit"
-          class="te-button te-button--submit"
-        }}
-      </div>
-    {{/if}}
-
-    {{#if isTuneSaveSuccess}}
-      {{#bs-alert type="success" class="te-form__banner te-form__banner--success"}}
-        <div class="te-form__banner-title">Success:</div> Filters modified.
-      {{/bs-alert}}
-    {{/if}}
-
-    {{#if isTuneSaveFailure}}
-      {{#bs-alert type="danger" class="te-form__banner te-form__banner--failure"}}
-        <span class="stronger">Error:</span> {{failureMessage}}
-      {{/bs-alert}}
-    {{/if}}
-
-  </div>
-
-  {{#if isTunePreviewActive}}
-    <div class="te-horizontal-cards te-content-block">
-      <h4 class="te-self-serve__block-title">{{removedAnomalies}} Anomalies removed with these settings</h4>
-      {{#if (gt removedAnomalies 0)}}
-        <p class="te-self-serve__block-subtext">The following previously sent anomalies <span class="stronger">would not have been sent</span> under the new settings.</p>
-      {{else}}
-        <p class="te-self-serve__block-subtext">These settings would result in <span class="stronger">NO reduction</span> of sent anomalies</p>
-      {{/if}}
-      {{#if isAnomalyTableLoading}}
-        <div class="spinner-wrapper-self-serve spinner-wrapper-self-serve__content-block">{{ember-spinner}}</div>
-      {{/if}}
-
-      {{!-- Alert anomaly table --}}
-      <table class="te-anomaly-table te-anomaly-table-tuning">
-        <thead>
-          <tr class="te-anomaly-table__row te-anomaly-table__row--metrics">
-            <td colspan="5">
-              <ul class="te-anomaly-table-stats">
-                {{#each tableStats as |metric|}}
-                  <li class="te-anomaly-table-stats__category {{if metric.isActive "te-anomaly-table-stats__category--active"}}" {{action "toggleCategory" metric.label}}>
-                    <div class="te-anomaly-table-stats__content te-anomaly-table-stats__content--number">
-                      {{metric.count}}
-                    </div>
-                    <div class="te-anomaly-table-stats__content te-anomaly-table-stats__content--text">
-                      {{metric.label}}
-                    </div>
-                  </li>
-                {{/each}}
-              </ul>
-            </td>
-          </tr>
-          <tr class="te-anomaly-table__row te-anomaly-table__head">
-             <th class="te-anomaly-table__cell-head">
-              <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "start"}}>
-                Start/Duration (PDT)
-                <i class="te-anomaly-table__icon glyphicon {{if sortColumnStartUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-              </a>
-             </th>
-             {{#if alertHasDimensions}}
-                <th class="te-anomaly-table__cell-head">Dimensions</th>
-             {{/if}}
-             <th class="te-anomaly-table__cell-head">
-              <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "score"}}>
-                Severity Score
-                <i class="te-anomaly-table__icon glyphicon {{if sortColumnScoreUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-              </a>
-             </th>
-             <th class="te-anomaly-table__cell-head">
-              <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "change"}}>
-                Current/WoW
-                <i class="te-anomaly-table__icon glyphicon {{if sortColumnChangeUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-              </a>
-             </th>
-             <th class="te-anomaly-table__cell-head">
-              <a class="te-anomaly-table__cell-link" {{action "toggleSortDirection" "resolution"}}>
-                Resolution
-                <i class="te-anomaly-table__icon glyphicon {{if sortColumnResolutionUp "glyphicon-menu-up" "glyphicon-menu-down"}}"></i>
-              </a>
-           </th>
-          </tr>
-        </thead>
-
-        <tbody>
-          {{#each diffedAnomalies as |anomaly|}}
-            <tr class="te-anomaly-table__row">
-               <td class="te-anomaly-table__cell">
-                <ul class="te-anomaly-table__list">
-                  <li class="te-anomaly-table__list-item te-anomaly-table__list-item--shadow">{{anomaly.index}}</li>
-                  <li class="te-anomaly-table__list-item te-anomaly-table__list-item--stronger">{{anomaly.startDateStr}}</li>
-                  <li class="te-anomaly-table__list-item te-anomaly-table__list-item--lighter">{{anomaly.durationStr}}</li>
-                </ul>
-               </td>
-               {{#if alertHasDimensions}}
-                 <td class="te-anomaly-table__cell">
-                  <ul class="te-anomaly-table__list">
-                   {{#each anomaly.dimensionList as |dimension|}}
-                      <li class="te-anomaly-table__list-item te-anomaly-table__list-item--smaller">
-                        {{dimension.dimensionKey}}: <span class="stronger">{{dimension.dimensionVal}}</span>
-                      </li>
-                   {{else}}
-                      -
-                   {{/each}}
-                  </ul>
-                 </td>
-               {{/if}}
-               <td class="te-anomaly-table__cell">{{anomaly.severityScore}}</td>
-               <td class="te-anomaly-table__cell">
-                 <ul class="te-anomaly-table__list">
-                    <li>{{anomaly.shownCurrent}} / {{anomaly.shownBaseline}}</li>
-                    <li class="te-anomaly-table__value-label te-anomaly-table__value-label--{{calculate-direction anomaly.shownChangeRate}}">
-                      ({{anomaly.shownChangeRate}}%)
-                    </li>
-                 </ul>
-               </td>
-               <td class="te-anomaly-table__cell">
-                  {{anomaly.feedbackLabel}}
-               </td>
-            </tr>
-          {{/each}}
-        </tbody>
-      </table>
-    </div>
-  {{/if}}
-
-</div>
diff --git a/thirdeye/thirdeye-frontend/app/pods/preview/controller.js b/thirdeye/thirdeye-frontend/app/pods/preview/controller.js
deleted file mode 100644
index 9430b53..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/preview/controller.js
+++ /dev/null
@@ -1,478 +0,0 @@
-import { observer, computed, set, get, getProperties } from '@ember/object';
-import { later, debounce } from '@ember/runloop';
-import { reads, gt, or } from '@ember/object/computed';
-import { inject as service } from '@ember/service';
-import Controller from '@ember/controller';
-import {
-  filterObject,
-  filterPrefix,
-  hasPrefix,
-  toBaselineUrn,
-  toBaselineRange,
-  toCurrentUrn,
-  toOffsetUrn,
-  toFilters,
-  toFilterMap,
-  appendFilters,
-  dateFormatFull,
-  colorMapping,
-  stripTail,
-  extractTail,
-  toColor
-} from 'thirdeye-frontend/utils/rca-utils';
-import EVENT_TABLE_COLUMNS from 'thirdeye-frontend/shared/eventTableColumns';
-import filterBarConfig from 'thirdeye-frontend/shared/filterBarConfig';
-import moment from 'moment';
-import config from 'thirdeye-frontend/config/environment';
-import _ from 'lodash';
-import { checkStatus } from 'thirdeye-frontend/utils/utils';
-import fetch from 'fetch';
-
-const PREVIEW_DATE_FORMAT = 'MMM DD, hh:mm a';
-
-export default Controller.extend({
-  detectionConfig: null,
-
-  detectionConfigName: null,
-
-  detectionConfigCron: null,
-
-  metricUrn: null,
-
-  output: 'nothing here',
-
-  anomalies: null,
-
-  diagnostics: null,
-
-  diagnosticsPath: null,
-
-  diagnosticsValues: null,
-
-  timeseries: null,
-
-  baseline: null,
-
-  analysisRange: [moment().subtract(2, 'month').startOf('hour').valueOf(), moment().startOf('hour').valueOf()],
-
-  displayRange: [moment().subtract(3, 'month').startOf('hour').valueOf(), moment().startOf('hour').valueOf()],
-
-  compareMode: 'wo1w',
-
-  compareModeOptions: [
-    'wo1w',
-    'wo2w',
-    'wo3w',
-    'wo4w',
-    'mean4w',
-    'median4w',
-    'min4w',
-    'max4w',
-    'none'
-  ],
-
-  errorTimeseries: null,
-
-  errorBaseline: null,
-
-  errorAnomalies: null,
-
-  colorMapping: colorMapping,
-
-  axis: {
-    y: {
-      show: true
-    },
-    y2: {
-      show: false
-    },
-    x: {
-      type: 'timeseries',
-      show: true,
-      tick: {
-        fit: false
-      }
-    }
-  },
-
-  zoom: {
-    enabled: true,
-    rescale: true
-  },
-
-  legend: {
-    show: false
-  },
-
-  anomalyMetricUrns: computed('anomalies', function () {
-    const anomalies = get(this, 'anomalies') || [];
-    const metricUrns = new Set(anomalies.map(anomaly => stripTail(anomaly.metricUrn)));
-
-    // TODO refactor this side-effect
-    this._fetchEntities(metricUrns)
-      .then(res => set(this, 'metricEntities', res));
-
-    return metricUrns;
-  }),
-
-  metricEntities: null,
-
-  anomalyMetricEntities: computed('anomalyMetricUrns', 'metricEntities', function () {
-    const { anomalyMetricUrns, metricEntities } = getProperties(this, 'anomalyMetricUrns', 'metricEntities');
-    if (_.isEmpty(anomalyMetricUrns) || _.isEmpty(metricEntities)) { return []; }
-    return [...anomalyMetricUrns].filter(urn => urn in metricEntities).map(urn => metricEntities[urn]).sortBy('name');
-  }),
-
-  anomalyMetricUrnDimensions: computed('anomalies', function () {
-    const anomalies = get(this, 'anomalies');
-    const urn2dimensions = {};
-    anomalies.forEach(anomaly => {
-      const baseUrn = stripTail(anomaly.metricUrn);
-      if (!_.isEqual(baseUrn, anomaly.metricUrn)) {
-        urn2dimensions[baseUrn] = urn2dimensions[baseUrn] || new Set();
-        urn2dimensions[baseUrn].add(anomaly.metricUrn);
-      }
-    });
-    return urn2dimensions;
-  }),
-
-  anomalyMetricUrnDimensionLabels: computed('anomalies', function () {
-    const anomalies = get(this, 'anomalies');
-    const metricUrns = new Set(anomalies.map(anomaly => anomaly.metricUrn));
-
-    const urn2count = {};
-    [...anomalies].forEach(anomaly => {
-      const urn = anomaly.metricUrn;
-      urn2count[urn] = (urn2count[urn] || 0) + 1;
-    });
-
-    const urn2labels = {};
-    [...metricUrns].forEach(urn => {
-      const filters = toFilters(urn);
-      urn2labels[urn] = filters.map(arr => arr[1]).join(", ") + ` (${urn2count[urn]})`;
-    });
-    return urn2labels;
-  }),
-
-  anomaliesByMetricUrn: computed('anomalies', function () {
-    const anomalies = get(this, 'anomalies');
-    const urn2anomalies = {};
-    anomalies.forEach(anomaly => {
-      const urn = anomaly.metricUrn;
-      urn2anomalies[urn] = (urn2anomalies[urn] || []).concat([anomaly]);
-    });
-    return urn2anomalies;
-  }),
-
-  series: computed(
-    'anomalies',
-    'timeseries',
-    'baseline',
-    'diagnosticsSeries',
-    'analysisRange',
-    'displayRange',
-    function () {
-      const metricUrn = get(this, 'metricUrn');
-      const anomalies = get(this, 'anomalies');
-      const timeseries = get(this, 'timeseries');
-      const baseline = get(this, 'baseline');
-      const diagnosticsSeries = get(this, 'diagnosticsSeries');
-      const analysisRange = get(this, 'analysisRange');
-      const displayRange = get(this, 'displayRange');
-
-      const series = {};
-
-      if (!_.isEmpty(anomalies)) {
-
-        anomalies
-          .filter(anomaly => anomaly.metricUrn === metricUrn)
-          .forEach(anomaly => {
-            const key = this._formatAnomaly(anomaly);
-            series[key] = {
-              timestamps: [anomaly.startTime, anomaly.endTime],
-              values: [1, 1],
-              type: 'line',
-              color: 'teal',
-              axis: 'y2'
-            };
-            series[key + '-region'] = Object.assign({}, series[key], {
-              type: 'region',
-              color: 'orange'
-            });
-          });
-      }
-
-      if (timeseries && !_.isEmpty(timeseries.value)) {
-        series['current'] = {
-          timestamps: timeseries.timestamp,
-          values: timeseries.value,
-          type: 'line',
-          color: toColor(metricUrn)
-        };
-      }
-
-      if (baseline && !_.isEmpty(baseline.value)) {
-        series['baseline'] = {
-          timestamps: baseline.timestamp,
-          values: baseline.value,
-          type: 'line',
-          color: 'light-' + toColor(metricUrn)
-        };
-      }
-
-      // detection range
-      if (timeseries && !_.isEmpty(timeseries.value)) {
-        series['pre-detection-region'] = {
-          timestamps: [displayRange[0], analysisRange[0]],
-          values: [1, 1],
-          type: 'region',
-          color: 'grey'
-        };
-      }
-
-      return Object.assign(series, diagnosticsSeries);
-    }
-  ),
-
-  diagnosticsSeries: computed(
-    'diagnostics',
-    'diagnosticsPath',
-    'diagnosticsValues',
-    function () {
-      const diagnosticsPath = get(this, 'diagnosticsPath');
-      const diagnosticsValues = get(this, 'diagnosticsValues') || [];
-
-      const series = {};
-
-      diagnosticsValues.forEach(value => {
-        const diagnostics = this._makeDiagnosticsSeries(diagnosticsPath, value);
-        if (!_.isEmpty(diagnostics)) {
-          series[`diagnostics-${value}`] = diagnostics;
-        }
-      });
-
-      const changeSeries = this._makeDiagnosticsPoints(diagnosticsPath);
-      Object.keys(changeSeries).forEach(key => {
-        series[`diagnostics-${key}`] = changeSeries[key];
-      });
-
-      return series;
-    }
-  ),
-
-  diagnosticsPathOptions: computed('diagnostics', function () {
-    const diagnostics = get(this, 'diagnostics');
-    return this._makePaths('', diagnostics);
-  }),
-
-  diagnosticsValueOptions: computed('diagnostics', 'diagnosticsPath', function () {
-    const diagnosticsPath = get(this, 'diagnosticsPath');
-    const diagnostics = get(this, 'diagnostics.' + diagnosticsPath);
-    if (_.isEmpty(diagnostics)) { return []; }
-    return Object.keys(diagnostics.data);
-  }),
-
-  _makePaths(prefix, diagnostics) {
-    if (_.isEmpty(diagnostics)) { return []; }
-
-    const directPaths = Object.keys(diagnostics)
-      .filter(key => key.startsWith('thirdeye:metric:'))
-      .map(key => prefix + `${key}`);
-
-    const nestedPaths = Object.keys(diagnostics)
-      .filter(key => !key.startsWith('thirdeye:metric:'))
-      .map(key => this._makePaths(`${prefix}${key}.`, diagnostics[key]))
-      .reduce((agg, paths) => agg.concat(paths), []);
-
-    return directPaths.concat(nestedPaths);
-  },
-
-  _makeKey(dimensions) {
-    return Object.values(dimensions).join(', ')
-  },
-
-  _formatAnomaly(anomaly) {
-    return `${moment(anomaly.startTime).format(PREVIEW_DATE_FORMAT)} (${this._makeKey(anomaly.dimensions)})`;
-  },
-
-  _filterAnomalies(rows) {
-    return rows.filter(row => (row.startTime && row.endTime && !row.child));
-  },
-
-  _makeDiagnosticsSeries(path, key) {
-    try {
-      const source = get(this, 'diagnostics.' + path + '.data');
-
-      if (_.isEmpty(source.timestamp) || _.isEmpty(source[key])) { return; }
-
-      return {
-        timestamps: source.timestamp,
-        values: source[key],
-        type: 'line',
-        axis: 'y2'
-      }
-
-    } catch (err) {
-      return undefined;
-    }
-  },
-
-  _makeDiagnosticsPoints(path) {
-    try {
-      const changepoints = get(this, 'diagnostics.' + path + '.changepoints');
-
-      if (_.isEmpty(changepoints)) { return {}; }
-
-      const out = {};
-      changepoints.forEach((p, i) => {
-        out[`changepoint-${i}`] = {
-          timestamps: [p, p + 1],
-          values: [1, 0],
-          type: 'line',
-          color: 'red',
-          axis: 'y2'
-        };
-
-        out[`changepoint-${i}-region`] = {
-          timestamps: [p, p + 3600000 * 24],
-          values: [1, 1],
-          type: 'region',
-          color: 'red',
-          axis: 'y2'
-        };
-      });
-
-      return out;
-
-    } catch (err) {
-      return {};
-    }
-  },
-
-  _fetchTimeseries() {
-    const metricUrn = get(this, 'metricUrn');
-    const range = get(this, 'displayRange');
-    const granularity = '15_MINUTES';
-    const timezone = moment.tz.guess();
-
-    set(this, 'errorTimeseries', null);
-
-    const urlCurrent = `/rootcause/metric/timeseries?urn=${metricUrn}&start=${range[0]}&end=${range[1]}&offset=current&granularity=${granularity}&timezone=${timezone}`;
-    fetch(urlCurrent)
-      .then(checkStatus)
-      .then(res => set(this, 'timeseries', res))
-      .then(res => set(this, 'output', 'got timeseries'))
-      // .catch(err => set(this, 'errorTimeseries', err));
-
-    set(this, 'errorBaseline', null);
-
-    const offset = get(this, 'compareMode');
-    const urlBaseline = `/rootcause/metric/timeseries?urn=${metricUrn}&start=${range[0]}&end=${range[1]}&offset=${offset}&granularity=${granularity}&timezone=${timezone}`;
-    fetch(urlBaseline)
-      .then(checkStatus)
-      .then(res => set(this, 'baseline', res))
-      .then(res => set(this, 'output', 'got baseline'))
-      // .catch(err => set(this, 'errorBaseline', err));
-  },
-
-  _fetchAnomalies() {
-    const analysisRange = get(this, 'analysisRange');
-    const url = `/detection/preview?start=${analysisRange[0]}&end=${analysisRange[1]}&diagnostics=true`;
-
-    const jsonString = get(this, 'detectionConfig');
-
-    set(this, 'errorAnomalies', null);
-
-    fetch(url, { method: 'POST', body: jsonString })
-      .then(checkStatus)
-      .then(res => {
-        set(this, 'anomalies', this._filterAnomalies(res.anomalies));
-        set(this, 'diagnostics', res.diagnostics);
-      })
-      .then(res => set(this, 'output', 'got anomalies'))
-      // .catch(err => set(this, 'errorAnomalies', err));
-  },
-
-  _fetchEntities(urns) {
-    const urnString = [...urns].join(',');
-    const url = `/rootcause/raw?framework=identity&urns=${urnString}`;
-    return fetch(url)
-      .then(checkStatus)
-      .then(res => res.reduce((agg, entity) => {
-        agg[entity.urn] = entity;
-        return agg;
-      }, {}));
-  },
-
-  _writeDetectionConfig() {
-    const detectionConfigBean = {
-      name: get(this, 'detectionConfigName'),
-      cron: get(this, 'detectionConfigCron'),
-      properties: JSON.parse(get(this, 'detectionConfig')),
-      lastTimestamp: 0
-    };
-
-    const jsonString = JSON.stringify(detectionConfigBean);
-
-    return fetch(`/thirdeye/entity?entityType=DETECTION_CONFIG`, { method: 'POST', body: jsonString })
-      .then(checkStatus)
-      .then(res => set(this, 'output', `saved '${detectionConfigBean.name}' as id ${res}`))
-      .catch(err => set(this, 'errorAnomalies', err));
-  },
-
-  actions: {
-    onPreview() {
-      set(this, 'output', 'loading anomalies ...');
-
-      this._fetchAnomalies();
-    },
-
-    onMetricChange(updates) {
-      set(this, 'output', 'fetching time series ...');
-
-      const metricUrns = filterPrefix(Object.keys(updates), 'thirdeye:metric:');
-
-      if (_.isEmpty(metricUrns)) { return; }
-
-      const metricUrn = metricUrns[0];
-
-      set(this, 'metricUrn', metricUrn);
-
-      this._fetchTimeseries();
-    },
-
-    onMetricLink(metricUrn) {
-      set(this, 'metricUrn', metricUrn);
-      this._fetchTimeseries();
-
-      // select matching diagnostics
-      const diagnosticsPathOptions = get(this, 'diagnosticsPathOptions');
-      const candidate = diagnosticsPathOptions.find(path => path.includes(metricUrn));
-      if (!_.isEmpty(candidate)) {
-        set(this, 'diagnosticsPath', candidate);
-      }
-    },
-
-    onCompareMode(compareMode) {
-      set(this, 'output', 'fetching time series ...');
-
-      set(this, 'compareMode', compareMode);
-
-      this._fetchTimeseries();
-    },
-
-    onDiagnosticsPath(diagnosticsPath) {
-      set(this, 'diagnosticsPath', diagnosticsPath);
-    },
-
-    onDiagnosticsValues(diagnosticsValues) {
-      set(this, 'diagnosticsValues', diagnosticsValues);
-    },
-
-    onSave() {
-      set(this, 'output', 'saving detection config ...');
-
-      this._writeDetectionConfig();
-    }
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/preview/route.js b/thirdeye/thirdeye-frontend/app/pods/preview/route.js
deleted file mode 100644
index 8cdaf28..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/preview/route.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import Route from '@ember/routing/route';
-import { inject as service } from '@ember/service';
-
-export default Route.extend({
-  session: service(),
-  actions: {
-    /**
-     * save session url for transition on login
-     * @method willTransition
-     */
-    willTransition(transition) {
-      //saving session url - TODO: add a util or service - lohuynh
-      if (transition.intent.name && transition.intent.name !== 'logout') {
-        this.set('session.store.fromUrl', {lastIntentTransition: transition});
-      }
-    },
-    error() {
-      // The `error` hook is also provided the failed
-      // `transition`, which can be stored and later
-      // `.retry()`d if desired.
-      return true;
-    },
-  }
-});
diff --git a/thirdeye/thirdeye-frontend/app/pods/preview/template.hbs b/thirdeye/thirdeye-frontend/app/pods/preview/template.hbs
deleted file mode 100644
index a993e11..0000000
--- a/thirdeye/thirdeye-frontend/app/pods/preview/template.hbs
+++ /dev/null
@@ -1,82 +0,0 @@
-<div class="container">
-  <div class="row">
-    <div class="col-xs-12">
-      ({{metricUrn}})
-    </div>
-  </div>
-  <div class="row">
-    <div class="col-xs-12 preview-chart">
-      {{timeseries-chart
-        series=series
-        colorMapping=colorMapping
-        axis=axis
-        zoom=zoom
-        legend=legend
-      }}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col-xs-4">
-      <label>diagnostics path</label>
-      {{#power-select
-        selected=diagnosticsPath
-        options=diagnosticsPathOptions
-        searchEnabled=false
-        triggerId="select-diagnostics-path"
-        onchange=(action "onDiagnosticsPath")
-      as |path|
-      }}
-        {{path}}
-      {{/power-select}}
-    </div>
-    <div class="col-xs-4">
-      <label>value key</label>
-      {{#power-select-multiple
-        selected=diagnosticsValues
-        options=diagnosticsValueOptions
-        triggerId="select-diagnostics-values"
-        onchange=(action "onDiagnosticsValues")
-      as |value|
-      }}
-        {{value}}
-      {{/power-select-multiple}}
-    </div>
-
-  </div>
-  <div class="row">
-    <div class="col-xs-6">
-      <label>Name</label>{{input value=detectionConfigName}}
-      <label>Cron</label>{{input value=detectionConfigCron}}
-      <button onClick={{action "onSave"}}>save</button>
-    </div>
-    <div class="col-xs-6">
-      {{output}}
-    </div>
-  </div>
-  <div class="row">
-    <div class="col-xs-6">
-      {{textarea-autosize
-        placeholder="Enter detection config here ..."
-        value=detectionConfig
-        cols=70
-        rows=16
-      }}
-      <button onClick={{action "onPreview"}}>preview</button>
-    </div>
-    <div class="col-xs-6">
-      <p>{{errorTimeseries}}</p>
-      <p>{{errorBaseline}}</p>
-      <p>{{errorAnomalies}}</p>
-      {{#each anomalyMetricEntities as | baseMetric |}}
-        <p>
-          <a {{action "onMetricLink" baseMetric.urn}}>{{baseMetric.label}}{{get-safe anomalyMetricUrnDimensionLabels baseMetric.urn}}</a>
-          <ol>
-          {{#each (get-safe anomalyMetricUrnDimensions baseMetric.urn) as | metric |}}
-            <li><a {{action "onMetricLink" metric}}>{{get-safe anomalyMetricUrnDimensionLabels metric}}</a></li>
-          {{/each}}
-          </ol>
-        </p>
-      {{/each}}
-    </div>
-  </div>
-</div>
\ No newline at end of file
diff --git a/thirdeye/thirdeye-frontend/app/router.js b/thirdeye/thirdeye-frontend/app/router.js
index b222611..a9d895b 100644
--- a/thirdeye/thirdeye-frontend/app/router.js
+++ b/thirdeye/thirdeye-frontend/app/router.js
@@ -23,11 +23,6 @@ Router.map(function() {
   this.route('anomalies');
 
   this.route('manage', function() {
-    this.route('alert', { path: 'alert/:alert_id' }, function() {
-      this.route('explore');
-      this.route('tune');
-      this.route('edit');
-    });
     this.route('alerts', function() {
       this.route('performance');
     });
@@ -43,8 +38,6 @@ Router.map(function() {
 
   this.route('screenshot', { path: 'screenshot/:anomaly_id' });
   this.route('rootcause');
-  this.route('preview');
-  this.route('auto-onboard');
 });
 
 export default Router;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org