diff --git a/app/assets/javascripts/api/analytics_api.js b/app/assets/javascripts/api/analytics_api.js
index fd9b0160b0df48699dbab712bb564d403e9c1d0b..11786f6c365a22fd9a0b59f05ed133008675b371 100644
--- a/app/assets/javascripts/api/analytics_api.js
+++ b/app/assets/javascripts/api/analytics_api.js
@@ -1,33 +1,32 @@
 import axios from '~/lib/utils/axios_utils';
 import { buildApiUrl } from './api_utils';
 
-const GROUP_VSA_PATH_BASE =
-  '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id';
-const PROJECT_VSA_PATH_BASE = '/:project_path/-/analytics/value_stream_analytics/value_streams';
+const PROJECT_VSA_PATH_BASE = '/:request_path/-/analytics/value_stream_analytics/value_streams';
 const PROJECT_VSA_STAGES_PATH = `${PROJECT_VSA_PATH_BASE}/:value_stream_id/stages`;
+const PROJECT_VSA_STAGE_DATA_PATH = `${PROJECT_VSA_STAGES_PATH}/:stage_id`;
 
-const buildProjectValueStreamPath = (projectPath, valueStreamId = null) => {
+const buildProjectValueStreamPath = (requestPath, valueStreamId = null) => {
   if (valueStreamId) {
     return buildApiUrl(PROJECT_VSA_STAGES_PATH)
-      .replace(':project_path', projectPath)
+      .replace(':request_path', requestPath)
       .replace(':value_stream_id', valueStreamId);
   }
-  return buildApiUrl(PROJECT_VSA_PATH_BASE).replace(':project_path', projectPath);
+  return buildApiUrl(PROJECT_VSA_PATH_BASE).replace(':request_path', requestPath);
 };
 
-const buildGroupValueStreamPath = ({ groupId, valueStreamId = null, stageId = null }) =>
-  buildApiUrl(GROUP_VSA_PATH_BASE)
-    .replace(':id', groupId)
+const buildValueStreamStageDataPath = ({ requestPath, valueStreamId = null, stageId = null }) =>
+  buildApiUrl(PROJECT_VSA_STAGE_DATA_PATH)
+    .replace(':request_path', requestPath)
     .replace(':value_stream_id', valueStreamId)
     .replace(':stage_id', stageId);
 
-export const getProjectValueStreams = (projectPath) => {
-  const url = buildProjectValueStreamPath(projectPath);
+export const getProjectValueStreams = (requestPath) => {
+  const url = buildProjectValueStreamPath(requestPath);
   return axios.get(url);
 };
 
-export const getProjectValueStreamStages = (projectPath, valueStreamId) => {
-  const url = buildProjectValueStreamPath(projectPath, valueStreamId);
+export const getProjectValueStreamStages = (requestPath, valueStreamId) => {
+  const url = buildProjectValueStreamPath(requestPath, valueStreamId);
   return axios.get(url);
 };
 
@@ -45,7 +44,15 @@ export const getProjectValueStreamMetrics = (requestPath, params) =>
  * When used for project level VSA, requests should include the `project_id` in the params object
  */
 
-export const getValueStreamStageMedian = ({ groupId, valueStreamId, stageId }, params = {}) => {
-  const stageBase = buildGroupValueStreamPath({ groupId, valueStreamId, stageId });
+export const getValueStreamStageMedian = ({ requestPath, valueStreamId, stageId }, params = {}) => {
+  const stageBase = buildValueStreamStageDataPath({ requestPath, valueStreamId, stageId });
   return axios.get(`${stageBase}/median`, { params });
 };
+
+export const getValueStreamStageRecords = (
+  { requestPath, valueStreamId, stageId },
+  params = {},
+) => {
+  const stageBase = buildValueStreamStageDataPath({ requestPath, valueStreamId, stageId });
+  return axios.get(`${stageBase}/records`, { params });
+};
diff --git a/app/assets/javascripts/cycle_analytics/components/base.vue b/app/assets/javascripts/cycle_analytics/components/base.vue
index e637bd0d8194347995231b7a54dceca28801c1e4..0dc221abb613fd7497b863596b0cc0ed222d1e9b 100644
--- a/app/assets/javascripts/cycle_analytics/components/base.vue
+++ b/app/assets/javascripts/cycle_analytics/components/base.vue
@@ -42,7 +42,7 @@ export default {
       'selectedStageError',
       'stages',
       'summary',
-      'startDate',
+      'daysInPast',
       'permissions',
     ]),
     ...mapGetters(['pathNavigationData']),
@@ -51,13 +51,15 @@ export default {
       return selectedStageEvents.length && !isLoadingStage && !isEmptyStage;
     },
     displayNotEnoughData() {
-      return this.selectedStageReady && this.isEmptyStage;
+      return !this.isLoadingStage && this.isEmptyStage;
     },
     displayNoAccess() {
-      return this.selectedStageReady && !this.isUserAllowed(this.selectedStage.id);
+      return (
+        !this.isLoadingStage && this.selectedStage?.id && !this.isUserAllowed(this.selectedStage.id)
+      );
     },
-    selectedStageReady() {
-      return !this.isLoadingStage && this.selectedStage;
+    displayPathNavigation() {
+      return this.isLoading || (this.selectedStage && this.pathNavigationData.length);
     },
     emptyStageTitle() {
       if (this.displayNoAccess) {
@@ -83,8 +85,8 @@ export default {
       'setSelectedStage',
       'setDateRange',
     ]),
-    handleDateSelect(startDate) {
-      this.setDateRange({ startDate });
+    handleDateSelect(daysInPast) {
+      this.setDateRange(daysInPast);
     },
     onSelectStage(stage) {
       this.setSelectedStage(stage);
@@ -101,15 +103,18 @@ export default {
   dayRangeOptions: [7, 30, 90],
   i18n: {
     dropdownText: __('Last %{days} days'),
+    pageTitle: __('Value Stream Analytics'),
+    recentActivity: __('Recent Project Activity'),
   },
 };
 </script>
 <template>
   <div class="cycle-analytics">
+    <h3>{{ $options.i18n.pageTitle }}</h3>
     <path-navigation
-      v-if="selectedStageReady"
+      v-if="displayPathNavigation"
       class="js-path-navigation gl-w-full gl-pb-2"
-      :loading="isLoading"
+      :loading="isLoading || isLoadingStage"
       :stages="pathNavigationData"
       :selected-stage="selectedStage"
       :with-stage-counts="false"
@@ -135,7 +140,7 @@ export default {
               <button class="dropdown-menu-toggle" data-toggle="dropdown" type="button">
                 <span class="dropdown-label">
                   <gl-sprintf :message="$options.i18n.dropdownText">
-                    <template #days>{{ startDate }}</template>
+                    <template #days>{{ daysInPast }}</template>
                   </gl-sprintf>
                   <gl-icon name="chevron-down" class="dropdown-menu-toggle-icon gl-top-3" />
                 </span>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
index 2e225d90f9c77380a08aa5d1284584810bc8b5ef..7b31e8d902d679d114f43091833812bac446c503 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_table.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
@@ -52,7 +52,7 @@ export default {
     selectedStage: {
       type: Object,
       required: false,
-      default: () => ({ custom: false }),
+      default: () => ({}),
     },
     isLoading: {
       type: Boolean,
@@ -102,7 +102,7 @@ export default {
   },
   computed: {
     isEmptyStage() {
-      return !this.stageEvents.length;
+      return !this.selectedStage || !this.stageEvents.length;
     },
     emptyStateTitleText() {
       return this.emptyStateTitle || NOT_ENOUGH_DATA_ERROR;
diff --git a/app/assets/javascripts/cycle_analytics/index.js b/app/assets/javascripts/cycle_analytics/index.js
index 615f96c3860901b3f6a00ef28f35ab6e0bb6b9eb..cce2edb24472054c4619d2d3e5997848a1e5a7c0 100644
--- a/app/assets/javascripts/cycle_analytics/index.js
+++ b/app/assets/javascripts/cycle_analytics/index.js
@@ -20,11 +20,9 @@ export default () => {
   store.dispatch('initializeVsa', {
     projectId: parseInt(projectId, 10),
     groupPath,
-    requestPath,
-    fullPath,
-    features: {
-      cycleAnalyticsForGroups:
-        (groupPath && gon?.licensed_features?.cycleAnalyticsForGroups) || false,
+    endpoints: {
+      requestPath,
+      fullPath,
     },
   });
 
diff --git a/app/assets/javascripts/cycle_analytics/store/actions.js b/app/assets/javascripts/cycle_analytics/store/actions.js
index 5a7dbbd28bb9a0c3916fed83b84b642a503141c6..fd606109151738656cb2b9df4bcd8ac9fbadcee3 100644
--- a/app/assets/javascripts/cycle_analytics/store/actions.js
+++ b/app/assets/javascripts/cycle_analytics/store/actions.js
@@ -1,29 +1,28 @@
 import {
   getProjectValueStreamStages,
   getProjectValueStreams,
-  getProjectValueStreamStageData,
   getProjectValueStreamMetrics,
   getValueStreamStageMedian,
+  getValueStreamStageRecords,
 } from '~/api/analytics_api';
 import createFlash from '~/flash';
 import { __ } from '~/locale';
-import {
-  DEFAULT_DAYS_TO_DISPLAY,
-  DEFAULT_VALUE_STREAM,
-  I18N_VSA_ERROR_STAGE_MEDIAN,
-} from '../constants';
+import { DEFAULT_VALUE_STREAM, I18N_VSA_ERROR_STAGE_MEDIAN } from '../constants';
 import * as types from './mutation_types';
 
 export const setSelectedValueStream = ({ commit, dispatch }, valueStream) => {
   commit(types.SET_SELECTED_VALUE_STREAM, valueStream);
-  return dispatch('fetchValueStreamStages');
+  return Promise.all([dispatch('fetchValueStreamStages'), dispatch('fetchCycleAnalyticsData')]);
 };
 
 export const fetchValueStreamStages = ({ commit, state }) => {
-  const { fullPath, selectedValueStream } = state;
+  const {
+    endpoints: { fullPath },
+    selectedValueStream: { id },
+  } = state;
   commit(types.REQUEST_VALUE_STREAM_STAGES);
 
-  return getProjectValueStreamStages(fullPath, selectedValueStream.id)
+  return getProjectValueStreamStages(fullPath, id)
     .then(({ data }) => commit(types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS, data))
     .catch(({ response: { status } }) => {
       commit(types.RECEIVE_VALUE_STREAM_STAGES_ERROR, status);
@@ -41,16 +40,11 @@ export const receiveValueStreamsSuccess = ({ commit, dispatch }, data = []) => {
 
 export const fetchValueStreams = ({ commit, dispatch, state }) => {
   const {
-    fullPath,
-    features: { cycleAnalyticsForGroups },
+    endpoints: { fullPath },
   } = state;
   commit(types.REQUEST_VALUE_STREAMS);
 
-  const stageRequests = ['setSelectedStage'];
-  if (cycleAnalyticsForGroups) {
-    stageRequests.push('fetchStageMedians');
-  }
-
+  const stageRequests = ['setSelectedStage', 'fetchStageMedians'];
   return getProjectValueStreams(fullPath)
     .then(({ data }) => dispatch('receiveValueStreamsSuccess', data))
     .then(() => Promise.all(stageRequests.map((r) => dispatch(r))))
@@ -58,9 +52,10 @@ export const fetchValueStreams = ({ commit, dispatch, state }) => {
       commit(types.RECEIVE_VALUE_STREAMS_ERROR, status);
     });
 };
-
 export const fetchCycleAnalyticsData = ({
-  state: { requestPath },
+  state: {
+    endpoints: { requestPath },
+  },
   getters: { legacyFilterParams },
   commit,
 }) => {
@@ -76,18 +71,10 @@ export const fetchCycleAnalyticsData = ({
     });
 };
 
-export const fetchStageData = ({
-  state: { requestPath, selectedStage },
-  getters: { legacyFilterParams },
-  commit,
-}) => {
+export const fetchStageData = ({ getters: { requestParams, filterParams }, commit }) => {
   commit(types.REQUEST_STAGE_DATA);
 
-  return getProjectValueStreamStageData({
-    requestPath,
-    stageId: selectedStage.id,
-    params: legacyFilterParams,
-  })
+  return getValueStreamStageRecords(requestParams, filterParams)
     .then(({ data }) => {
       // when there's a query timeout, the request succeeds but the error is encoded in the response data
       if (data?.error) {
@@ -134,22 +121,32 @@ export const setSelectedStage = ({ dispatch, commit, state: { stages } }, select
   return dispatch('fetchStageData');
 };
 
-const refetchData = (dispatch, commit) => {
-  commit(types.SET_LOADING, true);
+export const setLoading = ({ commit }, value) => commit(types.SET_LOADING, value);
+
+const refetchStageData = (dispatch) => {
   return Promise.resolve()
-    .then(() => dispatch('fetchValueStreams'))
-    .then(() => dispatch('fetchCycleAnalyticsData'))
-    .finally(() => commit(types.SET_LOADING, false));
+    .then(() => dispatch('setLoading', true))
+    .then(() =>
+      Promise.all([
+        dispatch('fetchCycleAnalyticsData'),
+        dispatch('fetchStageData'),
+        dispatch('fetchStageMedians'),
+      ]),
+    )
+    .finally(() => dispatch('setLoading', false));
 };
 
-export const setFilters = ({ dispatch, commit }) => refetchData(dispatch, commit);
+export const setFilters = ({ dispatch }) => refetchStageData(dispatch);
 
-export const setDateRange = ({ dispatch, commit }, { startDate = DEFAULT_DAYS_TO_DISPLAY }) => {
-  commit(types.SET_DATE_RANGE, { startDate });
-  return refetchData(dispatch, commit);
+export const setDateRange = ({ dispatch, commit }, daysInPast) => {
+  commit(types.SET_DATE_RANGE, daysInPast);
+  return refetchStageData(dispatch);
 };
 
 export const initializeVsa = ({ commit, dispatch }, initialData = {}) => {
   commit(types.INITIALIZE_VSA, initialData);
-  return refetchData(dispatch, commit);
+
+  return dispatch('setLoading', true)
+    .then(() => dispatch('fetchValueStreams'))
+    .finally(() => dispatch('setLoading', false));
 };
diff --git a/app/assets/javascripts/cycle_analytics/store/getters.js b/app/assets/javascripts/cycle_analytics/store/getters.js
index 66971ea8a2ea0fe4a72472e7b95d5603cc30d6aa..9faccabcaad4b769e6ac23886e4a70b219817f6b 100644
--- a/app/assets/javascripts/cycle_analytics/store/getters.js
+++ b/app/assets/javascripts/cycle_analytics/store/getters.js
@@ -13,11 +13,11 @@ export const pathNavigationData = ({ stages, medians, stageCounts, selectedStage
 
 export const requestParams = (state) => {
   const {
-    selectedStage: { id: stageId = null },
-    groupPath: groupId,
+    endpoints: { fullPath },
     selectedValueStream: { id: valueStreamId },
+    selectedStage: { id: stageId = null },
   } = state;
-  return { valueStreamId, groupId, stageId };
+  return { requestPath: fullPath, valueStreamId, stageId };
 };
 
 const dateRangeParams = ({ createdAfter, createdBefore }) => ({
@@ -25,15 +25,14 @@ const dateRangeParams = ({ createdAfter, createdBefore }) => ({
   created_before: createdBefore ? dateFormat(createdBefore, dateFormats.isoDate) : null,
 });
 
-export const legacyFilterParams = ({ startDate }) => {
+export const legacyFilterParams = ({ daysInPast }) => {
   return {
-    'cycle_analytics[start_date]': startDate,
+    'cycle_analytics[start_date]': daysInPast,
   };
 };
 
-export const filterParams = ({ id, ...rest }) => {
+export const filterParams = (state) => {
   return {
-    project_ids: [id],
-    ...dateRangeParams(rest),
+    ...dateRangeParams(state),
   };
 };
diff --git a/app/assets/javascripts/cycle_analytics/store/mutations.js b/app/assets/javascripts/cycle_analytics/store/mutations.js
index 50157cc3618d9e00eaa9f91b91b489b9d342d49f..65035c0ebb8cf4ce758d5b52439c86520bf7e19d 100644
--- a/app/assets/javascripts/cycle_analytics/store/mutations.js
+++ b/app/assets/javascripts/cycle_analytics/store/mutations.js
@@ -4,15 +4,11 @@ import { decorateData, formatMedianValues, calculateFormattedDayInPast } from '.
 import * as types from './mutation_types';
 
 export default {
-  [types.INITIALIZE_VSA](state, { requestPath, fullPath, groupPath, projectId, features }) {
-    state.requestPath = requestPath;
-    state.fullPath = fullPath;
-    state.groupPath = groupPath;
-    state.id = projectId;
+  [types.INITIALIZE_VSA](state, { endpoints }) {
+    state.endpoints = endpoints;
     const { now, past } = calculateFormattedDayInPast(DEFAULT_DAYS_TO_DISPLAY);
     state.createdBefore = now;
     state.createdAfter = past;
-    state.features = features;
   },
   [types.SET_LOADING](state, loadingState) {
     state.isLoading = loadingState;
@@ -23,9 +19,9 @@ export default {
   [types.SET_SELECTED_STAGE](state, stage) {
     state.selectedStage = stage;
   },
-  [types.SET_DATE_RANGE](state, { startDate }) {
-    state.startDate = startDate;
-    const { now, past } = calculateFormattedDayInPast(startDate);
+  [types.SET_DATE_RANGE](state, daysInPast) {
+    state.daysInPast = daysInPast;
+    const { now, past } = calculateFormattedDayInPast(daysInPast);
     state.createdBefore = now;
     state.createdAfter = past;
   },
@@ -50,25 +46,16 @@ export default {
   [types.REQUEST_CYCLE_ANALYTICS_DATA](state) {
     state.isLoading = true;
     state.hasError = false;
-    if (!state.features.cycleAnalyticsForGroups) {
-      state.medians = {};
-    }
   },
   [types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS](state, data) {
-    const { summary, medians } = decorateData(data);
-    if (!state.features.cycleAnalyticsForGroups) {
-      state.medians = formatMedianValues(medians);
-    }
-    state.permissions = data.permissions;
+    const { summary } = decorateData(data);
+    state.permissions = data?.permissions || {};
     state.summary = summary;
     state.hasError = false;
   },
   [types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR](state) {
     state.isLoading = false;
     state.hasError = true;
-    if (!state.features.cycleAnalyticsForGroups) {
-      state.medians = {};
-    }
   },
   [types.REQUEST_STAGE_DATA](state) {
     state.isLoadingStage = true;
@@ -76,7 +63,7 @@ export default {
     state.selectedStageEvents = [];
     state.hasError = false;
   },
-  [types.RECEIVE_STAGE_DATA_SUCCESS](state, { events = [] }) {
+  [types.RECEIVE_STAGE_DATA_SUCCESS](state, events = []) {
     state.isLoadingStage = false;
     state.isEmptyStage = !events.length;
     state.selectedStageEvents = events.map((ev) =>
diff --git a/app/assets/javascripts/cycle_analytics/store/state.js b/app/assets/javascripts/cycle_analytics/store/state.js
index 4d61077fb99fc6934b4bdeb498de7263693fc882..562b5d0a743a211ea3f32c7035e94c2a31eab3b0 100644
--- a/app/assets/javascripts/cycle_analytics/store/state.js
+++ b/app/assets/javascripts/cycle_analytics/store/state.js
@@ -1,11 +1,9 @@
 import { DEFAULT_DAYS_TO_DISPLAY } from '../constants';
 
 export default () => ({
-  features: {},
   id: null,
-  requestPath: '',
-  fullPath: '',
-  startDate: DEFAULT_DAYS_TO_DISPLAY,
+  endpoints: {},
+  daysInPast: DEFAULT_DAYS_TO_DISPLAY,
   createdAfter: null,
   createdBefore: null,
   stages: [],
@@ -23,5 +21,4 @@ export default () => ({
   isLoadingStage: false,
   isEmptyStage: false,
   permissions: {},
-  parentPath: null,
 });
diff --git a/app/assets/javascripts/cycle_analytics/utils.js b/app/assets/javascripts/cycle_analytics/utils.js
index 1f72291e97b8fb8de4bb2c3e209832928646a4a5..c941799a2ed3ae0f0a35ddd92a5480acd9228fbd 100644
--- a/app/assets/javascripts/cycle_analytics/utils.js
+++ b/app/assets/javascripts/cycle_analytics/utils.js
@@ -8,13 +8,11 @@ import { parseSeconds } from '~/lib/utils/datetime_utility';
 import { s__, sprintf } from '../locale';
 
 const mapToSummary = ({ value, ...rest }) => ({ ...rest, value: value || '-' });
-const mapToMedians = ({ name: id, value }) => ({ id, value });
 
 export const decorateData = (data = {}) => {
-  const { stats: stages, summary } = data;
+  const { summary } = data;
   return {
     summary: summary?.map((item) => mapToSummary(item)) || [],
-    medians: stages?.map((item) => mapToMedians(item)) || [],
   };
 };
 
diff --git a/ee/app/assets/javascripts/analytics/cycle_analytics/store/actions/stages.js b/ee/app/assets/javascripts/analytics/cycle_analytics/store/actions/stages.js
index 32a764b4a6193a938c78daf7167d1330177af7aa..348e4ecd8b9482c128f16386f9ea32585e01fddc 100644
--- a/ee/app/assets/javascripts/analytics/cycle_analytics/store/actions/stages.js
+++ b/ee/app/assets/javascripts/analytics/cycle_analytics/store/actions/stages.js
@@ -1,5 +1,5 @@
 import Api from 'ee/api';
-import { getValueStreamStageMedian } from '~/api/analytics_api';
+import { getGroupValueStreamStageMedian } from 'ee/api/analytics_api';
 import {
   I18N_VSA_ERROR_STAGES,
   I18N_VSA_ERROR_STAGE_MEDIAN,
@@ -59,7 +59,7 @@ export const receiveStageMedianValuesError = ({ commit }, error) => {
 };
 
 const fetchStageMedian = ({ groupId, valueStreamId, stageId, params }) =>
-  getValueStreamStageMedian({ groupId, valueStreamId, stageId }, params).then(({ data }) => {
+  getGroupValueStreamStageMedian({ groupId, valueStreamId, stageId }, params).then(({ data }) => {
     return {
       id: stageId,
       ...(data?.error
diff --git a/ee/app/assets/javascripts/api/analytics_api.js b/ee/app/assets/javascripts/api/analytics_api.js
new file mode 100644
index 0000000000000000000000000000000000000000..60aed799e914f15a6f02f4e0819e25d4c353b56c
--- /dev/null
+++ b/ee/app/assets/javascripts/api/analytics_api.js
@@ -0,0 +1,19 @@
+import { buildApiUrl } from '~/api/api_utils';
+import axios from '~/lib/utils/axios_utils';
+
+const GROUP_VSA_PATH_BASE =
+  '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id';
+
+const buildGroupValueStreamPath = ({ groupId, valueStreamId = null, stageId = null }) =>
+  buildApiUrl(GROUP_VSA_PATH_BASE)
+    .replace(':id', groupId)
+    .replace(':value_stream_id', valueStreamId)
+    .replace(':stage_id', stageId);
+
+export const getGroupValueStreamStageMedian = (
+  { groupId, valueStreamId, stageId },
+  params = {},
+) => {
+  const stageBase = buildGroupValueStreamPath({ groupId, valueStreamId, stageId });
+  return axios.get(`${stageBase}/median`, { params });
+};
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index 418247c88aa516e9b25bace27eabf3ec5d4bfd92..704adfa568dcce581894903c016251d95db554d3 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -46,9 +46,9 @@
         @build = create_cycle(user, project, issue, mr, milestone, pipeline)
         deploy_master(user, project)
 
-        issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.day)
+        issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.hour)
         merge_request = issue.merge_requests_closing_issues.first.merge_request
-        merge_request.update!(created_at: issue.metrics.first_associated_with_milestone_at + 1.day)
+        merge_request.update!(created_at: issue.metrics.first_associated_with_milestone_at + 1.hour)
         merge_request.metrics.update!(
           latest_build_started_at: 4.hours.ago,
           latest_build_finished_at: 3.hours.ago,
diff --git a/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap b/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap
deleted file mode 100644
index 771625a3e51ad40c2288603ac843b384274bc851..0000000000000000000000000000000000000000
--- a/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap
+++ /dev/null
@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Value stream analytics component isLoading = true renders the path navigation component with prop \`loading\` set to true 1`] = `"<path-navigation-stub loading=\\"true\\" stages=\\"\\" selectedstage=\\"[object Object]\\" class=\\"js-path-navigation gl-w-full gl-pb-2\\"></path-navigation-stub>"`;
diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js
index 5b01741c9e436ed81eadf42c48264b2058ce5784..c2c6b2a5d06caf42cbc930566628600b07fc9c53 100644
--- a/spec/frontend/cycle_analytics/base_spec.js
+++ b/spec/frontend/cycle_analytics/base_spec.js
@@ -8,7 +8,15 @@ import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
 import StageTable from '~/cycle_analytics/components/stage_table.vue';
 import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants';
 import initState from '~/cycle_analytics/store/state';
-import { selectedStage, issueEvents } from './mock_data';
+import {
+  permissions,
+  transformedProjectStagePathData,
+  selectedStage,
+  issueEvents,
+  createdBefore,
+  createdAfter,
+  currentGroup,
+} from './mock_data';
 
 const selectedStageEvents = issueEvents.events;
 const noDataSvgPath = 'path/to/no/data';
@@ -18,25 +26,31 @@ Vue.use(Vuex);
 
 let wrapper;
 
-function createStore({ initialState = {} }) {
+const defaultState = {
+  permissions,
+  currentGroup,
+  createdBefore,
+  createdAfter,
+};
+
+function createStore({ initialState = {}, initialGetters = {} }) {
   return new Vuex.Store({
     state: {
       ...initState(),
-      permissions: {
-        [selectedStage.id]: true,
-      },
+      ...defaultState,
       ...initialState,
     },
     getters: {
-      pathNavigationData: () => [],
+      pathNavigationData: () => transformedProjectStagePathData,
+      ...initialGetters,
     },
   });
 }
 
-function createComponent({ initialState } = {}) {
+function createComponent({ initialState, initialGetters } = {}) {
   return extendedWrapper(
     shallowMount(BaseComponent, {
-      store: createStore({ initialState }),
+      store: createStore({ initialState, initialGetters }),
       propsData: {
         noDataSvgPath,
         noAccessSvgPath,
@@ -57,16 +71,7 @@ const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('tit
 
 describe('Value stream analytics component', () => {
   beforeEach(() => {
-    wrapper = createComponent({
-      initialState: {
-        isLoading: false,
-        isLoadingStage: false,
-        isEmptyStage: false,
-        selectedStageEvents,
-        selectedStage,
-        selectedStageError: '',
-      },
-    });
+    wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents } });
   });
 
   afterEach(() => {
@@ -102,7 +107,7 @@ describe('Value stream analytics component', () => {
     });
 
     it('renders the path navigation component with prop `loading` set to true', () => {
-      expect(findPathNavigation().html()).toMatchSnapshot();
+      expect(findPathNavigation().props('loading')).toBe(true);
     });
 
     it('does not render the overview metrics', () => {
@@ -130,13 +135,19 @@ describe('Value stream analytics component', () => {
       expect(tableWrapper.exists()).toBe(true);
       expect(tableWrapper.find(GlLoadingIcon).exists()).toBe(true);
     });
+
+    it('renders the path navigation loading state', () => {
+      expect(findPathNavigation().props('loading')).toBe(true);
+    });
   });
 
   describe('isEmptyStage = true', () => {
+    const emptyStageParams = {
+      isEmptyStage: true,
+      selectedStage: { ...selectedStage, emptyStageText: 'This stage is empty' },
+    };
     beforeEach(() => {
-      wrapper = createComponent({
-        initialState: { selectedStage, isEmptyStage: true },
-      });
+      wrapper = createComponent({ initialState: emptyStageParams });
     });
 
     it('renders the empty stage with `Not enough data` message', () => {
@@ -147,8 +158,7 @@ describe('Value stream analytics component', () => {
       beforeEach(() => {
         wrapper = createComponent({
           initialState: {
-            selectedStage,
-            isEmptyStage: true,
+            ...emptyStageParams,
             selectedStageError: 'There is too much data to calculate',
           },
         });
@@ -164,7 +174,9 @@ describe('Value stream analytics component', () => {
     beforeEach(() => {
       wrapper = createComponent({
         initialState: {
+          selectedStage,
           permissions: {
+            ...permissions,
             [selectedStage.id]: false,
           },
         },
@@ -179,6 +191,7 @@ describe('Value stream analytics component', () => {
   describe('without a selected stage', () => {
     beforeEach(() => {
       wrapper = createComponent({
+        initialGetters: { pathNavigationData: () => [] },
         initialState: { selectedStage: null, isEmptyStage: true },
       });
     });
@@ -187,7 +200,7 @@ describe('Value stream analytics component', () => {
       expect(findStageTable().exists()).toBe(true);
     });
 
-    it('does not render the path navigation component', () => {
+    it('does not render the path navigation', () => {
       expect(findPathNavigation().exists()).toBe(false);
     });
 
diff --git a/spec/frontend/cycle_analytics/store/actions_spec.js b/spec/frontend/cycle_analytics/store/actions_spec.js
index 8a8dd374f8e6939370f158ad0892798a05d2a8de..28715aa87e846b7c361e4a386e6433f040806aeb 100644
--- a/spec/frontend/cycle_analytics/store/actions_spec.js
+++ b/spec/frontend/cycle_analytics/store/actions_spec.js
@@ -2,39 +2,23 @@ import axios from 'axios';
 import MockAdapter from 'axios-mock-adapter';
 import testAction from 'helpers/vuex_action_helper';
 import * as actions from '~/cycle_analytics/store/actions';
+import * as getters from '~/cycle_analytics/store/getters';
 import httpStatusCodes from '~/lib/utils/http_status';
 import { allowedStages, selectedStage, selectedValueStream } from '../mock_data';
 
 const mockRequestPath = 'some/cool/path';
 const mockFullPath = '/namespace/-/analytics/value_stream_analytics/value_streams';
 const mockStartDate = 30;
-const mockRequestedDataActions = ['fetchValueStreams', 'fetchCycleAnalyticsData'];
-const mockInitializeActionCommit = {
-  payload: { requestPath: mockRequestPath },
-  type: 'INITIALIZE_VSA',
-};
+const mockEndpoints = { fullPath: mockFullPath, requestPath: mockRequestPath };
 const mockSetDateActionCommit = { payload: { startDate: mockStartDate }, type: 'SET_DATE_RANGE' };
-const mockRequestedDataMutations = [
-  {
-    payload: true,
-    type: 'SET_LOADING',
-  },
-  {
-    payload: false,
-    type: 'SET_LOADING',
-  },
-];
-
-const features = {
-  cycleAnalyticsForGroups: true,
-};
+
+const defaultState = { ...getters, selectedValueStream };
 
 describe('Project Value Stream Analytics actions', () => {
   let state;
   let mock;
 
   beforeEach(() => {
-    state = {};
     mock = new MockAdapter(axios);
   });
 
@@ -45,28 +29,62 @@ describe('Project Value Stream Analytics actions', () => {
 
   const mutationTypes = (arr) => arr.map(({ type }) => type);
 
+  const mockFetchStageDataActions = [
+    { type: 'setLoading', payload: true },
+    { type: 'fetchCycleAnalyticsData' },
+    { type: 'fetchStageData' },
+    { type: 'fetchStageMedians' },
+    { type: 'setLoading', payload: false },
+  ];
+
   describe.each`
-    action                      | payload                             | expectedActions               | expectedMutations
-    ${'initializeVsa'}          | ${{ requestPath: mockRequestPath }} | ${mockRequestedDataActions}   | ${[mockInitializeActionCommit, ...mockRequestedDataMutations]}
-    ${'setDateRange'}           | ${{ startDate: mockStartDate }}     | ${mockRequestedDataActions}   | ${[mockSetDateActionCommit, ...mockRequestedDataMutations]}
-    ${'setSelectedStage'}       | ${{ selectedStage }}                | ${['fetchStageData']}         | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
-    ${'setSelectedValueStream'} | ${{ selectedValueStream }}          | ${['fetchValueStreamStages']} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
+    action                      | payload                         | expectedActions                                                              | expectedMutations
+    ${'setLoading'}             | ${true}                         | ${[]}                                                                        | ${[{ type: 'SET_LOADING', payload: true }]}
+    ${'setDateRange'}           | ${{ startDate: mockStartDate }} | ${mockFetchStageDataActions}                                                 | ${[mockSetDateActionCommit]}
+    ${'setFilters'}             | ${[]}                           | ${mockFetchStageDataActions}                                                 | ${[]}
+    ${'setSelectedStage'}       | ${{ selectedStage }}            | ${[{ type: 'fetchStageData' }]}                                              | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
+    ${'setSelectedValueStream'} | ${{ selectedValueStream }}      | ${[{ type: 'fetchValueStreamStages' }, { type: 'fetchCycleAnalyticsData' }]} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
   `('$action', ({ action, payload, expectedActions, expectedMutations }) => {
     const types = mutationTypes(expectedMutations);
-
     it(`will dispatch ${expectedActions} and commit ${types}`, () =>
       testAction({
         action: actions[action],
         state,
         payload,
         expectedMutations,
-        expectedActions: expectedActions.map((a) => ({ type: a })),
+        expectedActions,
       }));
   });
 
+  describe('initializeVsa', () => {
+    let mockDispatch;
+    let mockCommit;
+    const payload = { endpoints: mockEndpoints };
+
+    beforeEach(() => {
+      mockDispatch = jest.fn(() => Promise.resolve());
+      mockCommit = jest.fn();
+    });
+
+    it('will dispatch the setLoading and fetchValueStreams actions and commit INITIALIZE_VSA', async () => {
+      await actions.initializeVsa(
+        {
+          ...state,
+          dispatch: mockDispatch,
+          commit: mockCommit,
+        },
+        payload,
+      );
+      expect(mockCommit).toHaveBeenCalledWith('INITIALIZE_VSA', { endpoints: mockEndpoints });
+      expect(mockDispatch).toHaveBeenCalledWith('setLoading', true);
+      expect(mockDispatch).toHaveBeenCalledWith('fetchValueStreams');
+      expect(mockDispatch).toHaveBeenCalledWith('setLoading', false);
+    });
+  });
+
   describe('fetchCycleAnalyticsData', () => {
     beforeEach(() => {
-      state = { requestPath: mockRequestPath };
+      state = { endpoints: mockEndpoints };
       mock = new MockAdapter(axios);
       mock.onGet(mockRequestPath).reply(httpStatusCodes.OK);
     });
@@ -85,7 +103,7 @@ describe('Project Value Stream Analytics actions', () => {
 
     describe('with a failing request', () => {
       beforeEach(() => {
-        state = { requestPath: mockRequestPath };
+        state = { endpoints: mockEndpoints };
         mock = new MockAdapter(axios);
         mock.onGet(mockRequestPath).reply(httpStatusCodes.BAD_REQUEST);
       });
@@ -105,11 +123,12 @@ describe('Project Value Stream Analytics actions', () => {
   });
 
   describe('fetchStageData', () => {
-    const mockStagePath = `${mockRequestPath}/events/${selectedStage.name}`;
+    const mockStagePath = /value_streams\/\w+\/stages\/\w+\/records/;
 
     beforeEach(() => {
       state = {
-        requestPath: mockRequestPath,
+        ...defaultState,
+        endpoints: mockEndpoints,
         startDate: mockStartDate,
         selectedStage,
       };
@@ -131,7 +150,8 @@ describe('Project Value Stream Analytics actions', () => {
 
       beforeEach(() => {
         state = {
-          requestPath: mockRequestPath,
+          ...defaultState,
+          endpoints: mockEndpoints,
           startDate: mockStartDate,
           selectedStage,
         };
@@ -155,7 +175,8 @@ describe('Project Value Stream Analytics actions', () => {
     describe('with a failing request', () => {
       beforeEach(() => {
         state = {
-          requestPath: mockRequestPath,
+          ...defaultState,
+          endpoints: mockEndpoints,
           startDate: mockStartDate,
           selectedStage,
         };
@@ -179,8 +200,7 @@ describe('Project Value Stream Analytics actions', () => {
 
     beforeEach(() => {
       state = {
-        features,
-        fullPath: mockFullPath,
+        endpoints: mockEndpoints,
       };
       mock = new MockAdapter(axios);
       mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
@@ -199,26 +219,6 @@ describe('Project Value Stream Analytics actions', () => {
         ],
       }));
 
-    describe('with cycleAnalyticsForGroups=false', () => {
-      beforeEach(() => {
-        state = {
-          features: { cycleAnalyticsForGroups: false },
-          fullPath: mockFullPath,
-        };
-        mock = new MockAdapter(axios);
-        mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
-      });
-
-      it("does not dispatch the 'fetchStageMedians' request", () =>
-        testAction({
-          action: actions.fetchValueStreams,
-          state,
-          payload: {},
-          expectedMutations: [{ type: 'REQUEST_VALUE_STREAMS' }],
-          expectedActions: [{ type: 'receiveValueStreamsSuccess' }, { type: 'setSelectedStage' }],
-        }));
-    });
-
     describe('with a failing request', () => {
       beforeEach(() => {
         mock = new MockAdapter(axios);
@@ -271,7 +271,7 @@ describe('Project Value Stream Analytics actions', () => {
 
     beforeEach(() => {
       state = {
-        fullPath: mockFullPath,
+        endpoints: mockEndpoints,
         selectedValueStream,
       };
       mock = new MockAdapter(axios);
diff --git a/spec/frontend/cycle_analytics/store/mutations_spec.js b/spec/frontend/cycle_analytics/store/mutations_spec.js
index c2bc124d5ba46bbf33f645995f1cdbb677f2c612..dcbc2369983c8874825321c5293064c97c264c58 100644
--- a/spec/frontend/cycle_analytics/store/mutations_spec.js
+++ b/spec/frontend/cycle_analytics/store/mutations_spec.js
@@ -21,15 +21,12 @@ const convertedEvents = issueEvents.events;
 const mockRequestPath = 'fake/request/path';
 const mockCreatedAfter = '2020-06-18';
 const mockCreatedBefore = '2020-07-18';
-const features = {
-  cycleAnalyticsForGroups: true,
-};
 
 describe('Project Value Stream Analytics mutations', () => {
   useFakeDate(2020, 6, 18);
 
   beforeEach(() => {
-    state = { features };
+    state = {};
   });
 
   afterEach(() => {
@@ -61,25 +58,45 @@ describe('Project Value Stream Analytics mutations', () => {
     ${types.REQUEST_STAGE_MEDIANS}                | ${'medians'}             | ${{}}
     ${types.RECEIVE_STAGE_MEDIANS_ERROR}          | ${'medians'}             | ${{}}
   `('$mutation will set $stateKey to $value', ({ mutation, stateKey, value }) => {
-    mutations[mutation](state, {});
+    mutations[mutation](state);
 
     expect(state).toMatchObject({ [stateKey]: value });
   });
 
+  const mockInitialPayload = {
+    endpoints: { requestPath: mockRequestPath },
+    currentGroup: { title: 'cool-group' },
+    id: 1337,
+  };
+  const mockInitializedObj = {
+    endpoints: { requestPath: mockRequestPath },
+    createdAfter: mockCreatedAfter,
+    createdBefore: mockCreatedBefore,
+  };
+
   it.each`
-    mutation                                      | payload                                   | stateKey                 | value
-    ${types.INITIALIZE_VSA}                       | ${{ requestPath: mockRequestPath }}       | ${'requestPath'}         | ${mockRequestPath}
-    ${types.SET_DATE_RANGE}                       | ${{ startDate: DEFAULT_DAYS_TO_DISPLAY }} | ${'startDate'}           | ${DEFAULT_DAYS_TO_DISPLAY}
-    ${types.SET_DATE_RANGE}                       | ${{ startDate: DEFAULT_DAYS_TO_DISPLAY }} | ${'createdAfter'}        | ${mockCreatedAfter}
-    ${types.SET_DATE_RANGE}                       | ${{ startDate: DEFAULT_DAYS_TO_DISPLAY }} | ${'createdBefore'}       | ${mockCreatedBefore}
-    ${types.SET_LOADING}                          | ${true}                                   | ${'isLoading'}           | ${true}
-    ${types.SET_LOADING}                          | ${false}                                  | ${'isLoading'}           | ${false}
-    ${types.SET_SELECTED_VALUE_STREAM}            | ${selectedValueStream}                    | ${'selectedValueStream'} | ${selectedValueStream}
-    ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData}                                | ${'summary'}             | ${convertedData.summary}
-    ${types.RECEIVE_VALUE_STREAMS_SUCCESS}        | ${[selectedValueStream]}                  | ${'valueStreams'}        | ${[selectedValueStream]}
-    ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS}  | ${{ stages: rawValueStreamStages }}       | ${'stages'}              | ${valueStreamStages}
-    ${types.RECEIVE_VALUE_STREAMS_SUCCESS}        | ${[selectedValueStream]}                  | ${'valueStreams'}        | ${[selectedValueStream]}
-    ${types.RECEIVE_STAGE_MEDIANS_SUCCESS}        | ${rawStageMedians}                        | ${'medians'}             | ${formattedStageMedians}
+    mutation                | stateKey           | value
+    ${types.INITIALIZE_VSA} | ${'endpoints'}     | ${{ requestPath: mockRequestPath }}
+    ${types.INITIALIZE_VSA} | ${'createdAfter'}  | ${mockCreatedAfter}
+    ${types.INITIALIZE_VSA} | ${'createdBefore'} | ${mockCreatedBefore}
+  `('$mutation will set $stateKey', ({ mutation, stateKey, value }) => {
+    mutations[mutation](state, { ...mockInitialPayload });
+
+    expect(state).toMatchObject({ ...mockInitializedObj, [stateKey]: value });
+  });
+
+  it.each`
+    mutation                                      | payload                             | stateKey                 | value
+    ${types.SET_DATE_RANGE}                       | ${DEFAULT_DAYS_TO_DISPLAY}          | ${'daysInPast'}          | ${DEFAULT_DAYS_TO_DISPLAY}
+    ${types.SET_DATE_RANGE}                       | ${DEFAULT_DAYS_TO_DISPLAY}          | ${'createdAfter'}        | ${mockCreatedAfter}
+    ${types.SET_DATE_RANGE}                       | ${DEFAULT_DAYS_TO_DISPLAY}          | ${'createdBefore'}       | ${mockCreatedBefore}
+    ${types.SET_LOADING}                          | ${true}                             | ${'isLoading'}           | ${true}
+    ${types.SET_LOADING}                          | ${false}                            | ${'isLoading'}           | ${false}
+    ${types.SET_SELECTED_VALUE_STREAM}            | ${selectedValueStream}              | ${'selectedValueStream'} | ${selectedValueStream}
+    ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData}                          | ${'summary'}             | ${convertedData.summary}
+    ${types.RECEIVE_VALUE_STREAMS_SUCCESS}        | ${[selectedValueStream]}            | ${'valueStreams'}        | ${[selectedValueStream]}
+    ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS}  | ${{ stages: rawValueStreamStages }} | ${'stages'}              | ${valueStreamStages}
+    ${types.RECEIVE_STAGE_MEDIANS_SUCCESS}        | ${rawStageMedians}                  | ${'medians'}             | ${formattedStageMedians}
   `(
     '$mutation with $payload will set $stateKey to $value',
     ({ mutation, payload, stateKey, value }) => {
@@ -97,41 +114,10 @@ describe('Project Value Stream Analytics mutations', () => {
     });
 
     it.each`
-      mutation                            | payload                  | stateKey                 | value
-      ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${{ events: [] }}        | ${'isEmptyStage'}        | ${true}
-      ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${{ events: rawEvents }} | ${'selectedStageEvents'} | ${convertedEvents}
-      ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${{ events: rawEvents }} | ${'isEmptyStage'}        | ${false}
-    `(
-      '$mutation with $payload will set $stateKey to $value',
-      ({ mutation, payload, stateKey, value }) => {
-        mutations[mutation](state, payload);
-
-        expect(state).toMatchObject({ [stateKey]: value });
-      },
-    );
-  });
-
-  describe('with cycleAnalyticsForGroups=false', () => {
-    useFakeDate(2020, 6, 18);
-
-    beforeEach(() => {
-      state = { features: { cycleAnalyticsForGroups: false } };
-    });
-
-    const formattedMedians = {
-      code: '2d',
-      issue: '-',
-      plan: '21h',
-      review: '-',
-      staging: '2d',
-      test: '4h',
-    };
-
-    it.each`
-      mutation                                      | payload    | stateKey     | value
-      ${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'medians'} | ${formattedMedians}
-      ${types.REQUEST_CYCLE_ANALYTICS_DATA}         | ${{}}      | ${'medians'} | ${{}}
-      ${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR}   | ${{}}      | ${'medians'} | ${{}}
+      mutation                            | payload      | stateKey                 | value
+      ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${[]}        | ${'isEmptyStage'}        | ${true}
+      ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${rawEvents} | ${'selectedStageEvents'} | ${convertedEvents}
+      ${types.RECEIVE_STAGE_DATA_SUCCESS} | ${rawEvents} | ${'isEmptyStage'}        | ${false}
     `(
       '$mutation with $payload will set $stateKey to $value',
       ({ mutation, payload, stateKey, value }) => {