debian-mirror-gitlab/app/assets/javascripts/monitoring/components/dashboard.vue

610 lines
18 KiB
Vue
Raw Normal View History

2018-03-17 18:26:18 +05:30
<script>
2020-03-13 15:44:24 +05:30
import { debounce, pickBy } from 'lodash';
2020-01-01 13:55:28 +05:30
import { mapActions, mapState, mapGetters } from 'vuex';
2019-12-21 20:55:43 +05:30
import VueDraggable from 'vuedraggable';
2019-10-12 21:52:04 +05:30
import {
GlButton,
GlDropdown,
GlDropdownItem,
2020-03-13 15:44:24 +05:30
GlDropdownHeader,
GlDropdownDivider,
2019-10-12 21:52:04 +05:30
GlFormGroup,
GlModal,
2020-03-13 15:44:24 +05:30
GlLoadingIcon,
GlSearchBoxByType,
2019-10-12 21:52:04 +05:30
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
2020-01-01 13:55:28 +05:30
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
2019-12-26 22:10:19 +05:30
import { s__ } from '~/locale';
2019-12-21 20:55:43 +05:30
import createFlash from '~/flash';
2020-03-13 15:44:24 +05:30
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
2020-04-08 14:13:33 +05:30
import { mergeUrlParams, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
2019-09-04 21:01:54 +05:30
import invalidUrl from '~/lib/utils/invalid_url';
2020-03-13 15:44:24 +05:30
import Icon from '~/vue_shared/components/icon.vue';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
2018-05-09 12:01:36 +05:30
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
2020-01-01 13:55:28 +05:30
import GroupEmptyState from './group_empty_state.vue';
2020-03-13 15:44:24 +05:30
import DashboardsDropdown from './dashboards_dropdown.vue';
2019-12-21 20:55:43 +05:30
import TrackEventDirective from '~/vue_shared/directives/track_event';
2020-03-13 15:44:24 +05:30
import { getAddMetricTrackingOptions, timeRangeToUrl, timeRangeFromUrl } from '../utils';
2020-04-08 14:13:33 +05:30
import { metricStates } from '../constants';
import { defaultTimeRange, timeRanges } from '~/vue_shared/constants';
2019-03-02 22:35:43 +05:30
2018-05-09 12:01:36 +05:30
export default {
components: {
2019-12-21 20:55:43 +05:30
VueDraggable,
2019-09-30 21:07:59 +05:30
PanelType,
2018-11-08 19:23:39 +05:30
Icon,
2019-07-31 22:56:46 +05:30
GlButton,
2019-07-07 11:18:12 +05:30
GlDropdown,
2020-03-13 15:44:24 +05:30
GlLoadingIcon,
2019-07-07 11:18:12 +05:30
GlDropdownItem,
2020-03-13 15:44:24 +05:30
GlDropdownHeader,
GlDropdownDivider,
GlSearchBoxByType,
2019-10-12 21:52:04 +05:30
GlFormGroup,
2019-07-31 22:56:46 +05:30
GlModal,
2020-03-13 15:44:24 +05:30
2019-12-21 20:55:43 +05:30
DateTimePicker,
2020-03-13 15:44:24 +05:30
GraphGroup,
EmptyState,
GroupEmptyState,
DashboardsDropdown,
2019-07-31 22:56:46 +05:30
},
directives: {
2019-10-12 21:52:04 +05:30
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
2019-12-21 20:55:43 +05:30
TrackEvent: TrackEventDirective,
2018-05-09 12:01:36 +05:30
},
2020-03-13 15:44:24 +05:30
mixins: [glFeatureFlagsMixin()],
2018-05-09 12:01:36 +05:30
props: {
2019-09-04 21:01:54 +05:30
externalDashboardUrl: {
2019-07-31 22:56:46 +05:30
type: String,
required: false,
default: '',
},
2018-05-09 12:01:36 +05:30
hasMetrics: {
type: Boolean,
required: false,
default: true,
2018-03-17 18:26:18 +05:30
},
2020-03-13 15:44:24 +05:30
showHeader: {
type: Boolean,
required: false,
default: true,
},
2018-05-09 12:01:36 +05:30
showPanels: {
type: Boolean,
required: false,
default: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
documentationPath: {
type: String,
required: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
settingsPath: {
type: String,
required: true,
},
clustersPath: {
type: String,
required: true,
},
tagsPath: {
type: String,
required: true,
},
projectPath: {
type: String,
required: true,
},
2020-03-13 15:44:24 +05:30
logsPath: {
type: String,
required: false,
default: invalidUrl,
},
defaultBranch: {
type: String,
required: true,
},
2018-05-09 12:01:36 +05:30
metricsEndpoint: {
type: String,
required: true,
},
2019-09-04 21:01:54 +05:30
deploymentsEndpoint: {
2018-05-09 12:01:36 +05:30
type: String,
required: false,
default: null,
},
emptyGettingStartedSvgPath: {
type: String,
required: true,
},
emptyLoadingSvgPath: {
type: String,
required: true,
},
emptyNoDataSvgPath: {
type: String,
required: true,
},
2020-01-01 13:55:28 +05:30
emptyNoDataSmallSvgPath: {
type: String,
required: true,
},
2018-05-09 12:01:36 +05:30
emptyUnableToConnectSvgPath: {
type: String,
required: true,
},
2018-11-08 19:23:39 +05:30
currentEnvironmentName: {
type: String,
required: true,
},
2019-07-31 22:56:46 +05:30
customMetricsAvailable: {
type: Boolean,
required: false,
default: false,
},
customMetricsPath: {
type: String,
2019-09-30 21:07:59 +05:30
required: false,
default: invalidUrl,
2019-07-31 22:56:46 +05:30
},
validateQueryPath: {
type: String,
2019-09-30 21:07:59 +05:30
required: false,
default: invalidUrl,
2019-07-31 22:56:46 +05:30
},
2019-09-04 21:01:54 +05:30
dashboardEndpoint: {
type: String,
required: false,
default: invalidUrl,
},
2020-03-13 15:44:24 +05:30
dashboardsEndpoint: {
type: String,
required: false,
default: invalidUrl,
},
2019-09-30 21:07:59 +05:30
currentDashboard: {
type: String,
required: false,
default: '',
},
smallEmptyState: {
type: Boolean,
required: false,
default: false,
},
2019-12-04 20:38:33 +05:30
alertsEndpoint: {
type: String,
required: false,
default: null,
},
prometheusAlertsAvailable: {
type: Boolean,
required: false,
default: false,
},
2019-12-21 20:55:43 +05:30
rearrangePanelsAvailable: {
type: Boolean,
required: false,
default: false,
},
2018-05-09 12:01:36 +05:30
},
data() {
return {
state: 'gettingStarted',
2019-07-31 22:56:46 +05:30
formIsValid: null,
2020-03-13 15:44:24 +05:30
selectedTimeRange: timeRangeFromUrl() || defaultTimeRange,
2019-12-26 22:10:19 +05:30
hasValidDates: true,
2020-03-13 15:44:24 +05:30
timeRanges,
isRearrangingPanels: false,
2018-05-09 12:01:36 +05:30
};
},
2019-07-31 22:56:46 +05:30
computed: {
canAddMetrics() {
return this.customMetricsAvailable && this.customMetricsPath.length;
},
2019-09-04 21:01:54 +05:30
...mapState('monitoringDashboard', [
2019-12-26 22:10:19 +05:30
'dashboard',
2019-09-04 21:01:54 +05:30
'emptyState',
'showEmptyState',
'deploymentData',
'useDashboardEndpoint',
2019-09-30 21:07:59 +05:30
'allDashboards',
'additionalPanelTypesEnabled',
2020-03-13 15:44:24 +05:30
'environmentsLoading',
2019-09-04 21:01:54 +05:30
]),
2020-03-13 15:44:24 +05:30
...mapGetters('monitoringDashboard', ['getMetricStates', 'filteredEnvironments']),
2019-10-12 21:52:04 +05:30
firstDashboard() {
2020-03-13 15:44:24 +05:30
return this.allDashboards.length > 0 ? this.allDashboards[0] : {};
2019-12-26 22:10:19 +05:30
},
selectedDashboard() {
return this.allDashboards.find(d => d.path === this.currentDashboard) || this.firstDashboard;
2019-09-04 21:01:54 +05:30
},
2019-12-21 20:55:43 +05:30
showRearrangePanelsBtn() {
return !this.showEmptyState && this.rearrangePanelsAvailable;
},
2019-09-30 21:07:59 +05:30
addingMetricsAvailable() {
return IS_EE && this.canAddMetrics && !this.showEmptyState;
},
2019-12-26 22:10:19 +05:30
hasHeaderButtons() {
return (
this.addingMetricsAvailable ||
this.showRearrangePanelsBtn ||
this.selectedDashboard.can_edit ||
this.externalDashboardUrl.length
);
2019-09-30 21:07:59 +05:30
},
2020-03-13 15:44:24 +05:30
shouldShowEnvironmentsDropdownNoMatchedMsg() {
return !this.environmentsLoading && this.filteredEnvironments.length === 0;
},
2019-07-31 22:56:46 +05:30
},
2018-05-09 12:01:36 +05:30
created() {
2019-09-04 21:01:54 +05:30
this.setEndpoints({
2018-05-09 12:01:36 +05:30
metricsEndpoint: this.metricsEndpoint,
2019-09-04 21:01:54 +05:30
deploymentsEndpoint: this.deploymentsEndpoint,
dashboardEndpoint: this.dashboardEndpoint,
2020-03-13 15:44:24 +05:30
dashboardsEndpoint: this.dashboardsEndpoint,
2019-09-30 21:07:59 +05:30
currentDashboard: this.currentDashboard,
projectPath: this.projectPath,
2020-03-13 15:44:24 +05:30
logsPath: this.logsPath,
2018-05-09 12:01:36 +05:30
});
},
mounted() {
if (!this.hasMetrics) {
2019-09-04 21:01:54 +05:30
this.setGettingStartedEmptyState();
2018-05-09 12:01:36 +05:30
} else {
2020-03-13 15:44:24 +05:30
this.setTimeRange(this.selectedTimeRange);
this.fetchData();
2018-05-09 12:01:36 +05:30
}
},
methods: {
2019-09-04 21:01:54 +05:30
...mapActions('monitoringDashboard', [
2020-03-13 15:44:24 +05:30
'setTimeRange',
2019-09-04 21:01:54 +05:30
'fetchData',
'setGettingStartedEmptyState',
'setEndpoints',
2019-12-26 22:10:19 +05:30
'setPanelGroupMetrics',
2020-03-13 15:44:24 +05:30
'filterEnvironments',
2019-09-04 21:01:54 +05:30
]),
2020-01-01 13:55:28 +05:30
updatePanels(key, panels) {
2019-12-26 22:10:19 +05:30
this.setPanelGroupMetrics({
2020-01-01 13:55:28 +05:30
panels,
2019-12-26 22:10:19 +05:30
key,
});
},
2020-01-01 13:55:28 +05:30
removePanel(key, panels, graphIndex) {
2019-12-26 22:10:19 +05:30
this.setPanelGroupMetrics({
2020-01-01 13:55:28 +05:30
panels: panels.filter((v, i) => i !== graphIndex),
2019-12-26 22:10:19 +05:30
key,
});
},
2020-03-13 15:44:24 +05:30
onDateTimePickerInput(timeRange) {
redirectTo(timeRangeToUrl(timeRange));
},
onDateTimePickerInvalid() {
createFlash(
s__(
'Metrics|Link contains an invalid time window, please verify the link to see the requested time range.',
),
);
// As a fallback, switch to default time range instead
this.selectedTimeRange = defaultTimeRange;
2019-12-21 20:55:43 +05:30
},
2020-03-13 15:44:24 +05:30
2019-10-12 21:52:04 +05:30
generateLink(group, title, yLabel) {
const dashboard = this.currentDashboard || this.firstDashboard.path;
2020-03-13 15:44:24 +05:30
const params = pickBy({ dashboard, group, title, y_label: yLabel }, value => value != null);
2019-10-12 21:52:04 +05:30
return mergeUrlParams(params, window.location.href);
},
2019-07-31 22:56:46 +05:30
hideAddMetricModal() {
this.$refs.addMetricModal.hide();
2019-07-07 11:18:12 +05:30
},
2019-12-21 20:55:43 +05:30
toggleRearrangingPanels() {
this.isRearrangingPanels = !this.isRearrangingPanels;
},
2019-07-31 22:56:46 +05:30
setFormValidity(isValid) {
this.formIsValid = isValid;
},
2020-03-13 15:44:24 +05:30
debouncedEnvironmentsSearch: debounce(function environmentsSearchOnInput(searchTerm) {
this.filterEnvironments(searchTerm);
}, 500),
2019-07-31 22:56:46 +05:30
submitCustomMetricsForm() {
this.$refs.customMetricsForm.submit();
},
2020-01-01 13:55:28 +05:30
/**
* Return a single empty state for a group.
*
* If all states are the same a single state is returned to be displayed
* Except if the state is OK, in which case the group is displayed.
*
* @param {String} groupKey - Identifier for group
* @returns {String} state code from `metricStates`
*/
groupSingleEmptyState(groupKey) {
const states = this.getMetricStates(groupKey);
if (states.length === 1 && states[0] !== metricStates.OK) {
return states[0];
}
return null;
},
/**
* A group should be not collapsed if any metric is loaded (OK)
*
* @param {String} groupKey - Identifier for group
* @returns {Boolean} If the group should be collapsed
*/
collapseGroup(groupKey) {
// Collapse group if no data is available
return !this.getMetricStates(groupKey).includes(metricStates.OK);
},
getAddMetricTrackingOptions,
2020-03-13 15:44:24 +05:30
selectDashboard(dashboard) {
const params = {
dashboard: dashboard.path,
};
redirectTo(mergeUrlParams(params, window.location.href));
},
2020-04-08 14:13:33 +05:30
refreshDashboard() {
refreshCurrentPage();
},
2019-07-31 22:56:46 +05:30
},
addMetric: {
title: s__('Metrics|Add metric'),
modalId: 'add-metric',
2018-05-09 12:01:36 +05:30
},
};
2018-03-17 18:26:18 +05:30
</script>
<template>
2020-03-13 15:44:24 +05:30
<div class="prometheus-graphs" data-qa-selector="prometheus_graphs">
<div
v-if="showHeader"
ref="prometheusGraphsHeader"
class="prometheus-graphs-header gl-p-3 pb-0 border-bottom bg-gray-light"
>
2019-10-12 21:52:04 +05:30
<div class="row">
2020-03-13 15:44:24 +05:30
<gl-form-group
:label="__('Dashboard')"
label-size="sm"
label-for="monitor-dashboards-dropdown"
class="col-sm-12 col-md-6 col-lg-2"
>
<dashboards-dropdown
id="monitor-dashboards-dropdown"
class="mb-0 d-flex"
toggle-class="dropdown-menu-toggle"
:default-branch="defaultBranch"
:selected-dashboard="selectedDashboard"
@selectDashboard="selectDashboard($event)"
/>
</gl-form-group>
2019-10-12 21:52:04 +05:30
2020-03-13 15:44:24 +05:30
<gl-form-group
:label="s__('Metrics|Environment')"
label-size="sm"
label-for="monitor-environments-dropdown"
class="col-sm-6 col-md-6 col-lg-2"
>
<gl-dropdown
id="monitor-environments-dropdown"
ref="monitorEnvironmentsDropdown"
data-qa-selector="environments_dropdown"
class="mb-0 d-flex"
toggle-class="dropdown-menu-toggle"
menu-class="monitor-environment-dropdown-menu"
:text="currentEnvironmentName"
2019-07-07 11:18:12 +05:30
>
2020-03-13 15:44:24 +05:30
<div class="d-flex flex-column overflow-hidden">
<gl-dropdown-header class="monitor-environment-dropdown-header text-center">{{
__('Environment')
}}</gl-dropdown-header>
<gl-dropdown-divider />
<gl-search-box-by-type
ref="monitorEnvironmentsDropdownSearch"
class="m-2"
@input="debouncedEnvironmentsSearch"
/>
<gl-loading-icon
v-if="environmentsLoading"
ref="monitorEnvironmentsDropdownLoading"
:inline="true"
/>
<div v-else class="flex-fill overflow-auto">
<gl-dropdown-item
v-for="environment in filteredEnvironments"
:key="environment.id"
:active="environment.name === currentEnvironmentName"
active-class="is-active"
:href="environment.metrics_path"
>{{ environment.name }}</gl-dropdown-item
>
</div>
<div
v-show="shouldShowEnvironmentsDropdownNoMatchedMsg"
ref="monitorEnvironmentsDropdownMsg"
class="text-secondary no-matches-message"
2019-10-12 21:52:04 +05:30
>
2020-03-13 15:44:24 +05:30
{{ __('No matching results') }}
</div>
</div>
</gl-dropdown>
</gl-form-group>
2019-10-12 21:52:04 +05:30
2020-03-13 15:44:24 +05:30
<gl-form-group
:label="s__('Metrics|Show last')"
label-size="sm"
label-for="monitor-time-window-dropdown"
2020-04-08 14:13:33 +05:30
class="col-sm-auto col-md-auto col-lg-auto"
2020-03-13 15:44:24 +05:30
>
<date-time-picker
ref="dateTimePicker"
:value="selectedTimeRange"
:options="timeRanges"
@input="onDateTimePickerInput"
@invalid="onDateTimePickerInvalid"
/>
</gl-form-group>
2019-10-12 21:52:04 +05:30
2020-04-08 14:13:33 +05:30
<gl-form-group class="col-sm-2 col-md-2 col-lg-1 refresh-dashboard-button">
<gl-button
ref="refreshDashboardBtn"
v-gl-tooltip
variant="default"
:title="s__('Metrics|Reload this page')"
@click="refreshDashboard"
>
<icon name="repeat" />
</gl-button>
</gl-form-group>
2019-10-12 21:52:04 +05:30
<gl-form-group
2019-12-26 22:10:19 +05:30
v-if="hasHeaderButtons"
2019-10-12 21:52:04 +05:30
label-for="prometheus-graphs-dropdown-buttons"
2019-12-21 20:55:43 +05:30
class="dropdown-buttons col-md d-md-flex col-lg d-lg-flex align-items-end"
2019-07-31 22:56:46 +05:30
>
2019-10-12 21:52:04 +05:30
<div id="prometheus-graphs-dropdown-buttons">
2019-12-21 20:55:43 +05:30
<gl-button
v-if="showRearrangePanelsBtn"
:pressed="isRearrangingPanels"
variant="default"
class="mr-2 mt-1 js-rearrange-button"
@click="toggleRearrangingPanels"
2020-03-13 15:44:24 +05:30
>{{ __('Arrange charts') }}</gl-button
2019-12-21 20:55:43 +05:30
>
2019-10-12 21:52:04 +05:30
<gl-button
v-if="addingMetricsAvailable"
2020-01-01 13:55:28 +05:30
ref="addMetricBtn"
2019-10-12 21:52:04 +05:30
v-gl-modal="$options.addMetric.modalId"
2019-12-21 20:55:43 +05:30
variant="outline-success"
2020-04-08 14:13:33 +05:30
data-qa-selector="add_metric_button"
2020-01-01 13:55:28 +05:30
class="mr-2 mt-1"
2020-03-13 15:44:24 +05:30
>{{ $options.addMetric.title }}</gl-button
2019-10-12 21:52:04 +05:30
>
<gl-modal
v-if="addingMetricsAvailable"
ref="addMetricModal"
:modal-id="$options.addMetric.modalId"
:title="$options.addMetric.title"
>
<form ref="customMetricsForm" :action="customMetricsPath" method="post">
<custom-metrics-form-fields
:validate-query-path="validateQueryPath"
form-operation="post"
@formValidation="setFormValidity"
/>
</form>
<div slot="modal-footer">
<gl-button @click="hideAddMetricModal">{{ __('Cancel') }}</gl-button>
<gl-button
2020-01-01 13:55:28 +05:30
ref="submitCustomMetricsFormBtn"
v-track-event="getAddMetricTrackingOptions()"
2019-10-12 21:52:04 +05:30
:disabled="!formIsValid"
variant="success"
@click="submitCustomMetricsForm"
2020-03-13 15:44:24 +05:30
>{{ __('Save changes') }}</gl-button
2019-10-12 21:52:04 +05:30
>
</div>
</gl-modal>
2019-12-26 22:10:19 +05:30
<gl-button
v-if="selectedDashboard.can_edit"
class="mt-1 js-edit-link"
:href="selectedDashboard.project_blob_path"
2020-03-13 15:44:24 +05:30
>{{ __('Edit dashboard') }}</gl-button
2019-12-26 22:10:19 +05:30
>
2019-10-12 21:52:04 +05:30
<gl-button
v-if="externalDashboardUrl.length"
class="mt-1 js-external-dashboard-link"
variant="primary"
:href="externalDashboardUrl"
target="_blank"
rel="noopener noreferrer"
>
{{ __('View full dashboard') }}
<icon name="external-link" />
</gl-button>
</div>
</gl-form-group>
2018-11-08 19:23:39 +05:30
</div>
</div>
2019-10-12 21:52:04 +05:30
2019-09-30 21:07:59 +05:30
<div v-if="!showEmptyState">
<graph-group
2020-04-08 14:13:33 +05:30
v-for="(groupData, index) in dashboard.panelGroups"
2019-10-12 21:52:04 +05:30
:key="`${groupData.group}.${groupData.priority}`"
2019-09-30 21:07:59 +05:30
:name="groupData.group"
:show-panels="showPanels"
2020-01-01 13:55:28 +05:30
:collapse-group="collapseGroup(groupData.key)"
2019-07-07 11:18:12 +05:30
>
2020-03-13 15:44:24 +05:30
<vue-draggable
v-if="!groupSingleEmptyState(groupData.key)"
:value="groupData.panels"
group="metrics-dashboard"
:component-data="{ attrs: { class: 'row mx-0 w-100' } }"
:disabled="!isRearrangingPanels"
@input="updatePanels(groupData.key, $event)"
>
<div
v-for="(graphData, graphIndex) in groupData.panels"
:key="`panel-type-${graphIndex}`"
class="col-12 col-lg-6 px-2 mb-2 draggable"
:class="{ 'draggable-enabled': isRearrangingPanels }"
2019-12-21 20:55:43 +05:30
>
2020-03-13 15:44:24 +05:30
<div class="position-relative draggable-panel js-draggable-panel">
<div
v-if="isRearrangingPanels"
class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end"
@click="removePanel(groupData.key, groupData.panels, graphIndex)"
>
<a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')">
<icon name="close" />
</a>
2020-01-01 13:55:28 +05:30
</div>
2020-03-13 15:44:24 +05:30
<panel-type
:clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)"
:graph-data="graphData"
:alerts-endpoint="alertsEndpoint"
:prometheus-alerts-available="prometheusAlertsAvailable"
:index="`${index}-${graphIndex}`"
/>
2019-10-12 21:52:04 +05:30
</div>
2020-03-13 15:44:24 +05:30
</div>
</vue-draggable>
2020-01-01 13:55:28 +05:30
<div v-else class="py-5 col col-sm-10 col-md-8 col-lg-7 col-xl-6">
<group-empty-state
ref="empty-group"
:documentation-path="documentationPath"
:settings-path="settingsPath"
:selected-state="groupSingleEmptyState(groupData.key)"
:svg-path="emptyNoDataSmallSvgPath"
/>
</div>
2019-09-30 21:07:59 +05:30
</graph-group>
</div>
<empty-state
v-else
:selected-state="emptyState"
:documentation-path="documentationPath"
:settings-path="settingsPath"
:clusters-path="clustersPath"
:empty-getting-started-svg-path="emptyGettingStartedSvgPath"
:empty-loading-svg-path="emptyLoadingSvgPath"
:empty-no-data-svg-path="emptyNoDataSvgPath"
2020-01-01 13:55:28 +05:30
:empty-no-data-small-svg-path="emptyNoDataSmallSvgPath"
2019-09-30 21:07:59 +05:30
:empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
:compact="smallEmptyState"
/>
2018-03-17 18:26:18 +05:30
</div>
</template>