Commit 4dd08265 authored by Michael Lunøe's avatar Michael Lunøe Committed by Simon Knox

Refactor: Remove Instance level VSA

Remove code for the instance level Value Stream
Analytics page, which is no longer being used.

Ref MRs:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39368
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41580

Ref issues:
https://gitlab.com/gitlab-org/gitlab/-/issues/230631
https://gitlab.com/gitlab-org/gitlab/-/issues/223735
parent 0c796fdc
<script>
import { GlEmptyState } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import { PROJECTS_PER_PAGE } from '../constants';
import GroupsDropdownFilter from '../../shared/components/groups_dropdown_filter.vue';
import ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue';
import { SIMILARITY_ORDER, LAST_ACTIVITY_AT, DATE_RANGE_LIMIT } from '../../shared/constants';
import DateRange from '../../shared/components/daterange.vue';
......@@ -25,7 +23,6 @@ export default {
DateRange,
DurationChart,
GlEmptyState,
GroupsDropdownFilter,
ProjectsDropdownFilter,
StageTable,
TypeOfWorkCharts,
......@@ -50,10 +47,6 @@ export default {
type: String,
required: true,
},
hideGroupDropDown: {
type: Boolean,
required: true,
},
},
computed: {
...mapState([
......@@ -61,7 +54,7 @@ export default {
'isLoading',
'isLoadingStage',
'isEmptyStage',
'selectedGroup',
'currentGroup',
'selectedProjects',
'selectedStage',
'stages',
......@@ -87,10 +80,10 @@ export default {
]),
...mapGetters('customStages', ['customStageFormActive']),
shouldRenderEmptyState() {
return !this.selectedGroup && !this.isLoading;
return !this.currentGroup && !this.isLoading;
},
shouldDisplayFilters() {
return this.selectedGroup && !this.errorCode;
return !this.errorCode;
},
shouldDisplayDurationChart() {
return this.featureFlags.hasDurationChart && !this.hasNoAccessError;
......@@ -101,11 +94,6 @@ export default {
shouldDisplayPathNavigation() {
return this.featureFlags.hasPathNavigation && !this.hasNoAccessError && this.selectedStage;
},
shouldDisplayFilterBar() {
// TODO: After we remove instance VSA currentGroupPath will be always set
// https://gitlab.com/gitlab-org/gitlab/-/issues/223735
return this.currentGroupPath;
},
shouldDisplayCreateMultipleValueStreams() {
return Boolean(
this.featureFlags.hasCreateMultipleValueStreams && !this.isLoadingValueStreams,
......@@ -118,7 +106,6 @@ export default {
const selectedProjectIds = this.selectedProjectIds?.length ? this.selectedProjectIds : null;
return {
group_id: !this.hideGroupDropDown ? this.currentGroupPath : null,
project_ids: selectedProjectIds,
created_after: toYmd(this.startDate),
created_before: toYmd(this.endDate),
......@@ -143,26 +130,14 @@ export default {
...mapActions([
'fetchCycleAnalyticsData',
'fetchStageData',
'setSelectedGroup',
'setSelectedProjects',
'setSelectedStage',
'setDateRange',
'updateStage',
'removeStage',
'updateStage',
'reorderStage',
]),
...mapActions('customStages', [
'hideForm',
'showCreateForm',
'showEditForm',
'createStage',
'clearFormErrors',
]),
onGroupSelect(group) {
this.setSelectedGroup(group);
this.fetchCycleAnalyticsData();
},
...mapActions('customStages', ['hideForm', 'showCreateForm', 'showEditForm', 'createStage']),
onProjectsSelect(projects) {
this.setSelectedProjects(projects);
this.fetchCycleAnalyticsData();
......@@ -194,9 +169,6 @@ export default {
},
multiProjectSelect: true,
dateOptions: [7, 30, 90],
groupsQueryParams: {
min_access_level: featureAccessLevel.EVERYONE,
},
maxDateRange: DATE_RANGE_LIMIT,
};
</script>
......@@ -211,7 +183,15 @@ export default {
class="gl-align-self-start gl-sm-align-self-start gl-mt-0 gl-sm-mt-5"
/>
</div>
<div class="gl-max-w-full">
<gl-empty-state
v-if="shouldRenderEmptyState"
:title="__('Value Stream Analytics can help you determine your team’s velocity')"
:description="
__('Filter parameters are not valid. Make sure that the end date is after the start date.')
"
:svg-path="emptyStateSvgPath"
/>
<div v-if="!shouldRenderEmptyState" class="gl-max-w-full">
<div class="gl-mt-3 gl-py-2 gl-px-3 bg-gray-light border-top border-bottom">
<div v-if="shouldDisplayPathNavigation" class="gl-w-full gl-pb-2">
<path-navigation
......@@ -223,57 +203,35 @@ export default {
/>
</div>
<div
v-if="shouldDisplayFilters"
class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-justify-content-space-between"
>
<div class="dropdown-container d-flex flex-column flex-lg-row">
<groups-dropdown-filter
v-if="!hideGroupDropDown"
class="js-groups-dropdown-filter"
:class="{ 'mr-lg-3': shouldDisplayFilters }"
:query-params="$options.groupsQueryParams"
:default-group="selectedGroup"
@selected="onGroupSelect"
/>
<projects-dropdown-filter
v-if="shouldDisplayFilters"
:key="selectedGroup.id"
class="js-projects-dropdown-filter project-select"
:group-id="selectedGroup.id"
:query-params="projectsQueryParams"
:multi-select="$options.multiProjectSelect"
:default-projects="selectedProjects"
@selected="onProjectsSelect"
/>
</div>
<div v-if="shouldDisplayFilters" class="gl-justify-content-end gl-white-space-nowrap">
<date-range
:start-date="startDate"
:end-date="endDate"
:max-date-range="$options.maxDateRange"
:include-selected-date="true"
class="js-daterange-picker"
@change="setDateRange"
/>
</div>
<projects-dropdown-filter
:key="currentGroup.id"
class="js-projects-dropdown-filter project-select"
:group-id="currentGroup.id"
:query-params="projectsQueryParams"
:multi-select="$options.multiProjectSelect"
:default-projects="selectedProjects"
@selected="onProjectsSelect"
/>
<date-range
:start-date="startDate"
:end-date="endDate"
:max-date-range="$options.maxDateRange"
:include-selected-date="true"
class="js-daterange-picker"
@change="setDateRange"
/>
</div>
<filter-bar
v-if="shouldDisplayFilterBar"
v-if="shouldDisplayFilters"
class="js-filter-bar filtered-search-box gl-display-flex gl-mt-3 gl-mr-3 gl-border-none"
:group-path="currentGroupPath"
/>
</div>
</div>
<gl-empty-state
v-if="shouldRenderEmptyState"
:title="__('Value Stream Analytics can help you determine your team’s velocity')"
:description="
__(
'Start by choosing a group to see how your team is spending time. You can then drill down to the project level.',
)
"
:svg-path="emptyStateSvgPath"
/>
<div v-else class="cycle-analytics mt-0">
<div v-if="!shouldRenderEmptyState" class="cycle-analytics gl-mt-0">
<gl-empty-state
v-if="hasNoAccessError"
class="js-empty-state"
......
......@@ -28,7 +28,7 @@ export default {
startDate,
endDate,
selectedProjectIds,
selectedGroup: { name: groupName },
currentGroup: { name: groupName },
} = this.selectedTasksByTypeFilters;
const selectedProjectCount = selectedProjectIds.length;
......
......@@ -4,13 +4,12 @@ import CycleAnalytics from './components/base.vue';
import createStore from './store';
import { buildCycleAnalyticsInitialData } from '../shared/utils';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { parseBoolean } from '~/lib/utils/common_utils';
Vue.use(GlToast);
export default () => {
const el = document.querySelector('#js-cycle-analytics-app');
const { emptyStateSvgPath, noDataSvgPath, noAccessSvgPath, hideGroupDropDown } = el.dataset;
const { emptyStateSvgPath, noDataSvgPath, noAccessSvgPath } = el.dataset;
const initialData = buildCycleAnalyticsInitialData(el.dataset);
const store = createStore();
const {
......@@ -51,7 +50,6 @@ export default () => {
emptyStateSvgPath,
noDataSvgPath,
noAccessSvgPath,
hideGroupDropDown: parseBoolean(hideGroupDropDown),
},
}),
});
......
......@@ -14,16 +14,11 @@ import {
const appendExtension = path => (path.indexOf('.') > -1 ? path : `${path}.json`);
export const setPaths = ({ dispatch }, options) => {
const { group, milestonesPath = '', labelsPath = '' } = options;
// TODO: After we remove instance VSA we can rely on the paths from the BE
// https://gitlab.com/gitlab-org/gitlab/-/issues/223735
const groupPath = group?.parentId || group?.fullPath || '';
const milestonesEndpoint = milestonesPath || `/groups/${groupPath}/-/milestones`;
const labelsEndpoint = labelsPath || `/groups/${groupPath}/-/labels`;
const { groupPath, milestonesPath = '', labelsPath = '' } = options;
return dispatch('filters/setEndpoints', {
labelsEndpoint: appendExtension(labelsEndpoint),
milestonesEndpoint: appendExtension(milestonesEndpoint),
labelsEndpoint: appendExtension(labelsPath),
milestonesEndpoint: appendExtension(milestonesPath),
groupEndpoint: groupPath,
});
};
......@@ -31,11 +26,6 @@ export const setPaths = ({ dispatch }, options) => {
export const setFeatureFlags = ({ commit }, featureFlags) =>
commit(types.SET_FEATURE_FLAGS, featureFlags);
export const setSelectedGroup = ({ commit, dispatch }, group) => {
commit(types.SET_SELECTED_GROUP, group);
return dispatch('filters/initialize', { groupPath: group.full_path });
};
export const setSelectedProjects = ({ commit }, projects) =>
commit(types.SET_SELECTED_PROJECTS, projects);
......@@ -293,12 +283,13 @@ export const initializeCycleAnalytics = ({ dispatch, commit }, initialData = {})
selectedMilestone,
selectedAssigneeList,
selectedLabelList,
group,
} = initialData;
commit(types.SET_FEATURE_FLAGS, featureFlags);
if (initialData.group?.fullPath) {
if (group?.fullPath) {
return Promise.all([
dispatch('setPaths', { group: initialData.group, milestonesPath, labelsPath }),
dispatch('setPaths', { group, milestonesPath, labelsPath }),
dispatch('filters/initialize', {
selectedAuthor,
selectedMilestone,
......@@ -311,6 +302,7 @@ export const initializeCycleAnalytics = ({ dispatch, commit }, initialData = {})
.then(() => dispatch('fetchCycleAnalyticsData'))
.then(() => dispatch('initializeCycleAnalyticsSuccess'));
}
return dispatch('initializeCycleAnalyticsSuccess');
};
......
......@@ -11,7 +11,7 @@ export const hasNoAccessError = state => state.errorCode === httpStatus.FORBIDDE
export const currentValueStreamId = ({ selectedValueStream }) =>
selectedValueStream?.id || DEFAULT_VALUE_STREAM_ID;
export const currentGroupPath = ({ selectedGroup }) => selectedGroup?.fullPath || null;
export const currentGroupPath = ({ currentGroup }) => currentGroup?.fullPath || null;
export const selectedProjectIds = ({ selectedProjects }) =>
selectedProjects?.map(({ id }) => id) || [];
......
......@@ -2,9 +2,9 @@ import { getTasksByTypeData } from '../../../utils';
export const selectedTasksByTypeFilters = (state = {}, _, rootState = {}) => {
const { selectedLabelIds = [], subject } = state;
const { selectedGroup, selectedProjectIds = [], startDate = null, endDate = null } = rootState;
const { currentGroup, selectedProjectIds = [], startDate = null, endDate = null } = rootState;
return {
selectedGroup,
currentGroup,
selectedProjectIds,
startDate,
endDate,
......
export const SET_FEATURE_FLAGS = 'SET_FEATURE_FLAGS';
export const SET_SELECTED_GROUP = 'SET_SELECTED_GROUP';
export const SET_SELECTED_PROJECTS = 'SET_SELECTED_PROJECTS';
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
export const SET_DATE_RANGE = 'SET_DATE_RANGE';
......
......@@ -6,10 +6,6 @@ export default {
[types.SET_FEATURE_FLAGS](state, featureFlags) {
state.featureFlags = featureFlags;
},
[types.SET_SELECTED_GROUP](state, group) {
state.selectedGroup = convertObjectPropsToCamelCase(group, { deep: true });
state.selectedProjects = [];
},
[types.SET_SELECTED_PROJECTS](state, projects) {
state.selectedProjects = projects;
},
......@@ -91,14 +87,14 @@ export default {
[types.INITIALIZE_CYCLE_ANALYTICS](
state,
{
group: selectedGroup = null,
group = null,
createdAfter: startDate = null,
createdBefore: endDate = null,
selectedProjects = [],
} = {},
) {
state.isLoading = true;
state.selectedGroup = selectedGroup;
state.currentGroup = group;
state.selectedProjects = selectedProjects;
state.startDate = startDate;
state.endDate = endDate;
......
......@@ -13,7 +13,7 @@ export default () => ({
isSavingStageOrder: false,
errorSavingStageOrder: false,
selectedGroup: null,
currentGroup: null,
selectedProjects: [],
selectedStage: null,
selectedValueStream: null,
......
import initCycleAnalyticsApp from 'ee/analytics/cycle_analytics/index';
document.addEventListener('DOMContentLoaded', initCycleAnalyticsApp);
......@@ -2,7 +2,6 @@
- data_attributes = @request_params.valid? ? @request_params.to_data_attributes : {}
- api_paths = @group.present? ? { milestones_path: group_milestones_path(@group), labels_path: group_labels_path(@group) } : {}
- image_paths = { empty_state_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_data_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_access_svg_path: image_path("illustrations/analytics/no-access.svg")}
- settings = { hide_group_drop_down: 'true' }
- data_attributes.merge!(api_paths, image_paths, settings)
- data_attributes.merge!(api_paths, image_paths)
#js-cycle-analytics-app{ data: data_attributes }
......@@ -75,7 +75,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
it 'displays empty text' do
[
'Value Stream Analytics can help you determine your team’s velocity',
'Start by choosing a group to see how your team is spending time. You can then drill down to the project level.'
'Filter parameters are not valid. Make sure that the end date is after the start date.'
].each do |content|
expect(page).to have_content(content)
end
......
......@@ -52,7 +52,7 @@ export const group = {
avatar_url: `${TEST_HOST}/images/home/nasa.svg`,
};
export const selectedGroup = convertObjectPropsToCamelCase(group, { deep: true });
export const currentGroup = convertObjectPropsToCamelCase(group, { deep: true });
const getStageByTitle = (stages, title) =>
stages.find(stage => stage.title && stage.title.toLowerCase().trim() === title) || {};
......@@ -189,7 +189,7 @@ export const tasksByTypeData = {
};
export const taskByTypeFilters = {
selectedGroup: {
currentGroup: {
id: 22,
name: 'Gitlab Org',
fullName: 'Gitlab Org',
......
......@@ -63,11 +63,11 @@ describe('Cycle analytics getters', () => {
});
describe('currentGroupPath', () => {
describe('with selectedGroup set', () => {
describe('with currentGroup set', () => {
it('returns the `fullPath` value of the group', () => {
const fullPath = 'cool-beans';
state = {
selectedGroup: {
currentGroup: {
fullPath,
},
};
......@@ -76,9 +76,9 @@ describe('Cycle analytics getters', () => {
});
});
describe('without a selectedGroup set', () => {
describe('without a currentGroup set', () => {
it.each([[''], [{}], [null]])('given "%s" will return null', value => {
state = { selectedGroup: value };
state = { currentGroup: value };
expect(getters.currentGroupPath(state)).toEqual(null);
});
});
......@@ -88,7 +88,7 @@ describe('Cycle analytics getters', () => {
beforeEach(() => {
const fullPath = 'cool-beans';
state = {
selectedGroup: {
currentGroup: {
fullPath,
},
startDate,
......
......@@ -5,7 +5,7 @@ import * as actions from 'ee/analytics/cycle_analytics/store/modules/custom_stag
import * as types from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutation_types';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import { selectedGroup, endpoints, rawCustomStage } from '../../../mock_data';
import { currentGroup, endpoints, rawCustomStage } from '../../../mock_data';
jest.mock('~/flash');
......@@ -25,7 +25,7 @@ describe('Custom stage actions', () => {
afterEach(() => {
mock.restore();
state = { selectedGroup: null };
state = { currentGroup: null };
});
describe('createStage', () => {
......@@ -37,7 +37,7 @@ describe('Custom stage actions', () => {
};
beforeEach(() => {
state = { ...state, selectedGroup };
state = { ...state, currentGroup };
mock.onPost(endpoints.baseStagesEndpointstageData).reply(201, customStageData);
});
......@@ -70,7 +70,7 @@ describe('Custom stage actions', () => {
};
beforeEach(() => {
state = { ...state, selectedGroup };
state = { ...state, currentGroup };
mock
.onPost(endpoints.baseStagesEndpointstageData)
.reply(httpStatusCodes.UNPROCESSABLE_ENTITY, {
......
......@@ -62,7 +62,6 @@ describe('Cycle analytics mutations', () => {
it.each`
mutation | payload | expectedState
${types.SET_FEATURE_FLAGS} | ${{ hasDurationChart: true }} | ${{ featureFlags: { hasDurationChart: true } }}
${types.SET_SELECTED_GROUP} | ${{ fullPath: 'cool-beans' }} | ${{ selectedGroup: { fullPath: 'cool-beans' }, selectedProjects: [] }}
${types.SET_SELECTED_PROJECTS} | ${selectedProjects} | ${{ selectedProjects }}
${types.SET_DATE_RANGE} | ${{ startDate, endDate }} | ${{ startDate, endDate }}
${types.SET_SELECTED_STAGE} | ${{ id: 'first-stage' }} | ${{ selectedStage: { id: 'first-stage' } }}
......@@ -176,7 +175,6 @@ describe('Cycle analytics mutations', () => {
it.each`
stateKey | expectedState
${'isLoading'} | ${true}
${'selectedGroup'} | ${initialData.group}
${'selectedProjects'} | ${initialData.selectedProjects}
${'startDate'} | ${initialData.createdAfter}
${'endDate'} | ${initialData.createdBefore}
......
......@@ -11077,6 +11077,9 @@ msgstr ""
msgid "Filter by user"
msgstr ""
msgid "Filter parameters are not valid. Make sure that the end date is after the start date."
msgstr ""
msgid "Filter pipelines"
msgstr ""
......@@ -24107,9 +24110,6 @@ msgstr ""
msgid "Start and due date"
msgstr ""
msgid "Start by choosing a group to see how your team is spending time. You can then drill down to the project level."
msgstr ""
msgid "Start by choosing a group to start exploring the merge requests in that group. You can then proceed to filter by projects, labels, milestones and authors."
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment