Добавлены данные по Lead Time
parent
10078d9cb3
commit
1c0cfbb411
|
|
@ -33,6 +33,22 @@ function formatCompletion(value: number | null): string {
|
||||||
return `${value.toFixed(1)}%`
|
return `${value.toFixed(1)}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatLeadTimeInt(value: number | null): string {
|
||||||
|
if (value === null) {
|
||||||
|
return '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLeadTimePercentile(value: number | null): string {
|
||||||
|
if (value === null) {
|
||||||
|
return '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toFixed(1)
|
||||||
|
}
|
||||||
|
|
||||||
const sortedSprints = computed(() =>
|
const sortedSprints = computed(() =>
|
||||||
[...sprints.value].sort(
|
[...sprints.value].sort(
|
||||||
(left, right) =>
|
(left, right) =>
|
||||||
|
|
@ -76,8 +92,20 @@ watch(
|
||||||
<table class="sprint-table__table">
|
<table class="sprint-table__table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Спринт</th>
|
<th rowspan="3" class="sprint-table__th-sprint">Спринт</th>
|
||||||
<th>Выполнение</th>
|
<th rowspan="3" class="sprint-table__th-completion">Выполнение</th>
|
||||||
|
<th colspan="5" class="sprint-table__th-group">Lead Time</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2" class="sprint-table__th-subgroup">Границы</th>
|
||||||
|
<th colspan="3" class="sprint-table__th-subgroup">Персентиль</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="sprint-table__th-metric">max</th>
|
||||||
|
<th class="sprint-table__th-metric">min</th>
|
||||||
|
<th class="sprint-table__th-metric">95</th>
|
||||||
|
<th class="sprint-table__th-metric">80</th>
|
||||||
|
<th class="sprint-table__th-metric">50</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -100,6 +128,11 @@ watch(
|
||||||
{{ formatCompletion(sprint.sprint_completion_percentage) }}
|
{{ formatCompletion(sprint.sprint_completion_percentage) }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="sprint-table__metric">{{ formatLeadTimeInt(sprint.lead_time_max) }}</td>
|
||||||
|
<td class="sprint-table__metric">{{ formatLeadTimeInt(sprint.lead_time_min) }}</td>
|
||||||
|
<td class="sprint-table__metric">{{ formatLeadTimePercentile(sprint.lead_time_p95) }}</td>
|
||||||
|
<td class="sprint-table__metric">{{ formatLeadTimePercentile(sprint.lead_time_p80) }}</td>
|
||||||
|
<td class="sprint-table__metric">{{ formatLeadTimePercentile(sprint.lead_time_p50) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -131,16 +164,44 @@ watch(
|
||||||
.sprint-table__table td {
|
.sprint-table__table td {
|
||||||
padding: 14px 18px;
|
padding: 14px 18px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sprint-table__table th {
|
.sprint-table__table th {
|
||||||
color: rgba(241, 244, 247, 0.5);
|
color: #f1f4f7;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8125rem;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprint-table__th-sprint,
|
||||||
|
.sprint-table__th-completion {
|
||||||
|
text-align: left;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
|
color: rgba(241, 244, 247, 0.5);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprint-table__th-group {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprint-table__th-subgroup {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprint-table__th-metric {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprint-table__metric {
|
||||||
|
text-align: center;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: #f1f4f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sprint-table__table tbody tr:last-child td {
|
.sprint-table__table tbody tr:last-child td {
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,9 @@ export interface Sprint {
|
||||||
sprint_activate_datetime: string
|
sprint_activate_datetime: string
|
||||||
sprint_complete_datetime: string
|
sprint_complete_datetime: string
|
||||||
sprint_completion_percentage: number | null
|
sprint_completion_percentage: number | null
|
||||||
|
lead_time_max: number | null
|
||||||
|
lead_time_min: number | null
|
||||||
|
lead_time_p95: number | null
|
||||||
|
lead_time_p80: number | null
|
||||||
|
lead_time_p50: number | null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,20 +36,37 @@ final class ReportsApi {
|
||||||
* sprint_name: string,
|
* sprint_name: string,
|
||||||
* sprint_activate_datetime: string,
|
* sprint_activate_datetime: string,
|
||||||
* sprint_complete_datetime: string,
|
* sprint_complete_datetime: string,
|
||||||
* sprint_completion_percentage: float|null
|
* sprint_completion_percentage: float|null,
|
||||||
|
* lead_time_max: int|null,
|
||||||
|
* lead_time_min: int|null,
|
||||||
|
* lead_time_p95: float|null,
|
||||||
|
* lead_time_p80: float|null,
|
||||||
|
* lead_time_p50: float|null
|
||||||
* }>
|
* }>
|
||||||
*/
|
*/
|
||||||
public function getSprintsByTeamId(int $teamId): array {
|
public function getSprintsByTeamId(int $teamId): array {
|
||||||
$rows = $this->db->getRowset(
|
$rows = $this->db->getRowset(
|
||||||
'SELECT id,
|
'SELECT s.id,
|
||||||
jira_sprint_id,
|
s.jira_sprint_id,
|
||||||
sprint_name,
|
s.sprint_name,
|
||||||
sprint_activate_datetime,
|
s.sprint_activate_datetime,
|
||||||
sprint_complete_datetime,
|
s.sprint_complete_datetime,
|
||||||
sprint_completion_percentage
|
s.sprint_completion_percentage,
|
||||||
FROM sprints
|
MAX(t.lead_time_days) AS lead_time_max,
|
||||||
WHERE team_id = :team_id
|
MIN(t.lead_time_days) AS lead_time_min,
|
||||||
ORDER BY sprint_activate_datetime ASC',
|
percentile_cont(0.95) WITHIN GROUP (ORDER BY t.lead_time_days) AS lead_time_p95,
|
||||||
|
percentile_cont(0.80) WITHIN GROUP (ORDER BY t.lead_time_days) AS lead_time_p80,
|
||||||
|
percentile_cont(0.50) WITHIN GROUP (ORDER BY t.lead_time_days) AS lead_time_p50
|
||||||
|
FROM sprints s
|
||||||
|
LEFT JOIN tasks t ON t.sprint_id = s.id AND t.lead_time_days IS NOT NULL
|
||||||
|
WHERE s.team_id = :team_id
|
||||||
|
GROUP BY s.id,
|
||||||
|
s.jira_sprint_id,
|
||||||
|
s.sprint_name,
|
||||||
|
s.sprint_activate_datetime,
|
||||||
|
s.sprint_complete_datetime,
|
||||||
|
s.sprint_completion_percentage
|
||||||
|
ORDER BY s.sprint_activate_datetime ASC',
|
||||||
['team_id' => $teamId]
|
['team_id' => $teamId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -63,6 +80,11 @@ final class ReportsApi {
|
||||||
'sprint_completion_percentage' => $row['sprint_completion_percentage'] !== null
|
'sprint_completion_percentage' => $row['sprint_completion_percentage'] !== null
|
||||||
? (float) $row['sprint_completion_percentage']
|
? (float) $row['sprint_completion_percentage']
|
||||||
: null,
|
: null,
|
||||||
|
'lead_time_max' => $row['lead_time_max'] !== null ? (int) $row['lead_time_max'] : null,
|
||||||
|
'lead_time_min' => $row['lead_time_min'] !== null ? (int) $row['lead_time_min'] : null,
|
||||||
|
'lead_time_p95' => $row['lead_time_p95'] !== null ? (float) $row['lead_time_p95'] : null,
|
||||||
|
'lead_time_p80' => $row['lead_time_p80'] !== null ? (float) $row['lead_time_p80'] : null,
|
||||||
|
'lead_time_p50' => $row['lead_time_p50'] !== null ? (float) $row['lead_time_p50'] : null,
|
||||||
],
|
],
|
||||||
$rows
|
$rows
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue