diff options
Diffstat (limited to 'app/assets/javascripts/cycle_analytics/utils.js')
-rw-r--r-- | app/assets/javascripts/cycle_analytics/utils.js | 106 |
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) : '-', + }; + }, {}); |