diff --git a/changelog.d/fixed-10658.md b/changelog.d/fixed-10658.md new file mode 100644 index 0000000000..97b1243072 --- /dev/null +++ b/changelog.d/fixed-10658.md @@ -0,0 +1 @@ +- dashboard: Overview median-age KPI names its population and basis (#10658) diff --git a/src/changelog.d/fixed-overview-median-age-label.md b/src/changelog.d/fixed-overview-median-age-label.md new file mode 100644 index 0000000000..546d208059 --- /dev/null +++ b/src/changelog.d/fixed-overview-median-age-label.md @@ -0,0 +1 @@ +- Clarify the Overview median-age KPI label and tooltip so it names the actionable open issue/PR population and age basis. diff --git a/src/pkg/dashboard/overview_kpi_sparklines_test.go b/src/pkg/dashboard/overview_kpi_sparklines_test.go index 0e061fc445..2af1edd4d4 100644 --- a/src/pkg/dashboard/overview_kpi_sparklines_test.go +++ b/src/pkg/dashboard/overview_kpi_sparklines_test.go @@ -163,6 +163,8 @@ assert.equal((markup.match(/class="overview-kpi"/g) || []).length, 6); assert.equal((markup.match(/39<\/span>Total open issues<\/span>/); assert.match(markup, />31<\/span>Total open PRs<\/span>/); +assert.match(markup, />—<\/span>Median actionable age \(updated\)<\/span>/); +assert.match(markup, /Median time since updated for actionable open issues and PRs in the selected repos \(not MTTR, which measures time-to-resolve for closed items\)\./); for (const key of ['open-issues','open-prs','actionable-now','held','blocked-needs-human','median-age']) { assert.match(markup, new RegExp('overview-kpi:' + key)); } diff --git a/src/pkg/dashboard/overview_kpi_values_10139_test.go b/src/pkg/dashboard/overview_kpi_values_10139_test.go index 1748c0f454..67478252c4 100644 --- a/src/pkg/dashboard/overview_kpi_values_10139_test.go +++ b/src/pkg/dashboard/overview_kpi_values_10139_test.go @@ -74,7 +74,7 @@ assert.deepEqual(Object.fromEntries(values), { 'Actionable now (issues and PRs)': '103', 'Held': '1', 'Blocked / needs-human': '1', - 'Median age': '7m', + 'Median actionable age (updated)': '7m', }); repos[0].issues = 123; @@ -83,6 +83,10 @@ const rawOut = renderOverviewKPIs(repos, issueSlices, prSlices, state); const rawValues = Object.fromEntries([...rawOut.matchAll(/]*>([^<]*)<\/span>([^<]*)<\/span>/g)].map(m => [m[2], m[1]])); assert.equal(rawValues['Total open issues'], '123'); assert.equal(rawValues['Total open PRs'], '45'); +assert.match(out, /title="Median time since updated for actionable open issues and PRs in the selected repos \(not MTTR, which measures time-to-resolve for closed items\)\."/); +const createdOut = renderOverviewKPIs(repos, issueSlices, prSlices, { showKPIs: true, timeBasis: 'created' }); +assert.match(createdOut, />7m<\/span>Median actionable age \(created\)<\/span>/); +assert.match(createdOut, /title="Median time since created for actionable open issues and PRs in the selected repos \(not MTTR, which measures time-to-resolve for closed items\)\."/); ` out, err := exec.Command(node, "-e", script).CombinedOutput() if err != nil { diff --git a/src/pkg/dashboard/static/index.html b/src/pkg/dashboard/static/index.html index caacfa4996..715d06a217 100644 --- a/src/pkg/dashboard/static/index.html +++ b/src/pkg/dashboard/static/index.html @@ -13348,7 +13348,7 @@

What sh { key: 'actionable-now', field: 'overviewActionable', label: 'Actionable now (issues and PRs)', value: actionableNow, rawValue: actionableNow, color: 'var(--accent)', tip: 'Items in bands that can move without waiting.' }, { key: 'held', field: 'overviewHeld', label: 'Held', value: held, rawValue: held, color: 'color-mix(in srgb, var(--orange) 65%, var(--muted))', tip: 'Issues and PRs deliberately paused by a hold.' }, { key: 'blocked-needs-human', field: 'overviewBlockedHuman', label: 'Blocked / needs-human', value: blockedNeedsHuman, rawValue: blockedNeedsHuman, color: 'var(--red)', tip: 'Waiting, blocked, or escalated work needing a person or dependency.' }, - { key: 'median-age', field: 'overviewMedianAgeSec', label: 'Median age', value: age, rawValue: ageSec, color: 'var(--purple)', sparkFmt: fmtDurationFromSeconds, tip: 'Median selected actionable item age by the configured time basis.' } + { key: 'median-age', field: 'overviewMedianAgeSec', label: `Median actionable age (${state.timeBasis === 'created' ? 'created' : 'updated'})`, value: age, rawValue: ageSec, color: 'var(--purple)', sparkFmt: fmtDurationFromSeconds, tip: `Median time since ${state.timeBasis === 'created' ? 'created' : 'updated'} for actionable open issues and PRs in the selected repos (not MTTR, which measures time-to-resolve for closed items).` } ]; overviewKPIRecordLocal(overviewKPICurrentSample(cards)); return `
${overviewKPIWindowControls()}${cards.map(card => {