Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/cycle_analytics/utils.js')
-rw-r--r--app/assets/javascripts/cycle_analytics/utils.js106
1 files changed, 100 insertions, 6 deletions
diff --git a/app/assets/javascripts/cycle_analytics/utils.js b/app/assets/javascripts/cycle_analytics/utils.js
index 3afe4b021be..eeb3b8f8793 100644
--- a/app/assets/javascripts/cycle_analytics/utils.js
+++ b/app/assets/javascripts/cycle_analytics/utils.js
@@ -1,6 +1,9 @@
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { unescape } from 'lodash';
+import { sanitize } from '~/lib/dompurify';
+import { roundToNearestHalf, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { parseSeconds } from '~/lib/utils/datetime_utility';
import { dasherize } from '~/lib/utils/text_utility';
-import { __ } from '../locale';
+import { __, s__, sprintf } from '../locale';
import DEFAULT_EVENT_OBJECTS from './default_event_objects';
const EMPTY_STAGE_TEXTS = {
@@ -40,10 +43,17 @@ const mapToEvent = (event, stage) => {
export const decorateEvents = (events, stage) => events.map((event) => mapToEvent(event, stage));
-const mapToStage = (permissions, item) => {
- const slug = dasherize(item.name.toLowerCase());
+/*
+ * NOTE: We currently use the `name` field since the project level stages are in memory
+ * once we migrate to a default value stream https://gitlab.com/gitlab-org/gitlab/-/issues/326705
+ * we can use the `id` to identify which median we are using
+ */
+const mapToStage = (permissions, { name, ...rest }) => {
+ const slug = dasherize(name.toLowerCase());
return {
- ...item,
+ ...rest,
+ name,
+ id: name,
slug,
active: false,
isUserAllowed: permissions[slug],
@@ -53,11 +63,95 @@ const mapToStage = (permissions, item) => {
};
const mapToSummary = ({ value, ...rest }) => ({ ...rest, value: value || '-' });
+const mapToMedians = ({ id, value }) => ({ id, value });
export const decorateData = (data = {}) => {
const { permissions, stats, summary } = data;
+ const stages = stats?.map((item) => mapToStage(permissions, item)) || [];
return {
- stages: stats?.map((item) => mapToStage(permissions, item)) || [],
+ stages,
summary: summary?.map((item) => mapToSummary(item)) || [],
+ medians: stages?.map((item) => mapToMedians(item)) || [],
};
};
+
+/**
+ * Takes the stages and median data, combined with the selected stage, to build an
+ * array which is formatted to proivde the data required for the path navigation.
+ *
+ * @param {Array} stages - The stages available to the group / project
+ * @param {Object} medians - The median values for the stages available to the group / project
+ * @param {Object} stageCounts - The total item count for the stages available
+ * @param {Object} selectedStage - The currently selected stage
+ * @returns {Array} An array of stages formatted with data required for the path navigation
+ */
+export const transformStagesForPathNavigation = ({
+ stages,
+ medians,
+ stageCounts = {},
+ selectedStage,
+}) => {
+ const formattedStages = stages.map((stage) => {
+ return {
+ metric: medians[stage?.id],
+ selected: stage.id === selectedStage.id,
+ stageCount: stageCounts && stageCounts[stage?.id],
+ icon: null,
+ ...stage,
+ };
+ });
+
+ return formattedStages;
+};
+
+export const timeSummaryForPathNavigation = ({ seconds, hours, days, minutes, weeks, months }) => {
+ if (months) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}M'), {
+ value: roundToNearestHalf(months),
+ });
+ } else if (weeks) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}w'), {
+ value: roundToNearestHalf(weeks),
+ });
+ } else if (days) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}d'), {
+ value: roundToNearestHalf(days),
+ });
+ } else if (hours) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}h'), { value: hours });
+ } else if (minutes) {
+ return sprintf(s__('ValueStreamAnalytics|%{value}m'), { value: minutes });
+ } else if (seconds) {
+ return unescape(sanitize(s__('ValueStreamAnalytics|<1m'), { ALLOWED_TAGS: [] }));
+ }
+ return '-';
+};
+
+/**
+ * Takes a raw median value in seconds and converts it to a string representation
+ * ie. converts 172800 => 2d (2 days)
+ *
+ * @param {Number} Median - The number of seconds for the median calculation
+ * @returns {String} String representation ie 2w
+ */
+export const medianTimeToParsedSeconds = (value) =>
+ timeSummaryForPathNavigation({
+ ...parseSeconds(value, { daysPerWeek: 7, hoursPerDay: 24 }),
+ seconds: value,
+ });
+
+/**
+ * Takes the raw median value arrays and converts them into a useful object
+ * containing the string for display in the path navigation
+ * ie. converts [{ id: 'test', value: 172800 }] => { 'test': '2d' }
+ *
+ * @param {Array} Medians - Array of stage median objects, each contains a `id`, `value` and `error`
+ * @returns {Object} Returns key value pair with the stage name and its display median value
+ */
+export const formatMedianValues = (medians = []) =>
+ medians.reduce((acc, { id, value = 0 }) => {
+ return {
+ ...acc,
+ [id]: value ? medianTimeToParsedSeconds(value) : '-',
+ };
+ }, {});