jirametrics 2.13 → 2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/jirametrics-mcp +5 -0
- data/lib/jirametrics/aggregate_config.rb +10 -2
- data/lib/jirametrics/aging_work_bar_chart.rb +191 -133
- data/lib/jirametrics/aging_work_in_progress_chart.rb +43 -11
- data/lib/jirametrics/aging_work_table.rb +9 -7
- data/lib/jirametrics/anonymizer.rb +81 -6
- data/lib/jirametrics/atlassian_document_format.rb +101 -97
- data/lib/jirametrics/bar_chart_range.rb +17 -0
- data/lib/jirametrics/blocked_stalled_change.rb +5 -3
- data/lib/jirametrics/board.rb +32 -8
- data/lib/jirametrics/board_config.rb +4 -1
- data/lib/jirametrics/board_feature.rb +14 -0
- data/lib/jirametrics/board_movement_calculator.rb +2 -2
- data/lib/jirametrics/cfd_data_builder.rb +108 -0
- data/lib/jirametrics/change_item.rb +14 -6
- data/lib/jirametrics/chart_base.rb +141 -3
- data/lib/jirametrics/css_variable.rb +1 -1
- data/lib/jirametrics/cumulative_flow_diagram.rb +208 -0
- data/lib/jirametrics/{cycletime_config.rb → cycle_time_config.rb} +21 -4
- data/lib/jirametrics/cycletime_histogram.rb +15 -101
- data/lib/jirametrics/cycletime_scatterplot.rb +17 -83
- data/lib/jirametrics/daily_view.rb +85 -53
- data/lib/jirametrics/daily_wip_by_age_chart.rb +4 -5
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +14 -4
- data/lib/jirametrics/daily_wip_by_parent_chart.rb +4 -2
- data/lib/jirametrics/daily_wip_chart.rb +30 -8
- data/lib/jirametrics/data_quality_report.rb +43 -12
- data/lib/jirametrics/dependency_chart.rb +6 -3
- data/lib/jirametrics/download_config.rb +15 -0
- data/lib/jirametrics/downloader.rb +117 -100
- data/lib/jirametrics/downloader_for_cloud.rb +287 -0
- data/lib/jirametrics/downloader_for_data_center.rb +95 -0
- data/lib/jirametrics/estimate_accuracy_chart.rb +42 -4
- data/lib/jirametrics/examples/aggregated_project.rb +2 -2
- data/lib/jirametrics/examples/standard_project.rb +41 -28
- data/lib/jirametrics/expedited_chart.rb +3 -1
- data/lib/jirametrics/exporter.rb +26 -6
- data/lib/jirametrics/file_config.rb +9 -11
- data/lib/jirametrics/file_system.rb +59 -3
- data/lib/jirametrics/fix_version.rb +13 -0
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +5 -1
- data/lib/jirametrics/github_gateway.rb +115 -0
- data/lib/jirametrics/groupable_issue_chart.rb +11 -1
- data/lib/jirametrics/grouping_rules.rb +26 -4
- data/lib/jirametrics/html/aging_work_bar_chart.erb +5 -5
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +3 -1
- data/lib/jirametrics/html/aging_work_table.erb +5 -0
- data/lib/jirametrics/html/collapsible_issues_panel.erb +2 -2
- data/lib/jirametrics/html/cumulative_flow_diagram.erb +503 -0
- data/lib/jirametrics/html/daily_wip_chart.erb +40 -5
- data/lib/jirametrics/html/estimate_accuracy_chart.erb +4 -12
- data/lib/jirametrics/html/expedited_chart.erb +6 -14
- data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +4 -8
- data/lib/jirametrics/html/index.css +249 -69
- data/lib/jirametrics/html/index.erb +9 -35
- data/lib/jirametrics/html/index.js +164 -0
- data/lib/jirametrics/html/legacy_colors.css +174 -0
- data/lib/jirametrics/html/sprint_burndown.erb +17 -15
- data/lib/jirametrics/html/throughput_chart.erb +42 -11
- data/lib/jirametrics/html/{cycletime_histogram.erb → time_based_histogram.erb} +61 -59
- data/lib/jirametrics/html/{cycletime_scatterplot.erb → time_based_scatterplot.erb} +15 -11
- data/lib/jirametrics/html/wip_by_column_chart.erb +250 -0
- data/lib/jirametrics/html_generator.rb +32 -0
- data/lib/jirametrics/html_report_config.rb +52 -57
- data/lib/jirametrics/issue.rb +304 -101
- data/lib/jirametrics/issue_printer.rb +97 -0
- data/lib/jirametrics/jira_gateway.rb +77 -17
- data/lib/jirametrics/mcp_server.rb +531 -0
- data/lib/jirametrics/project_config.rb +128 -12
- data/lib/jirametrics/pull_request.rb +30 -0
- data/lib/jirametrics/pull_request_cycle_time_histogram.rb +77 -0
- data/lib/jirametrics/pull_request_cycle_time_scatterplot.rb +88 -0
- data/lib/jirametrics/pull_request_review.rb +13 -0
- data/lib/jirametrics/raw_javascript.rb +17 -0
- data/lib/jirametrics/settings.json +5 -1
- data/lib/jirametrics/sprint.rb +12 -0
- data/lib/jirametrics/sprint_burndown.rb +10 -4
- data/lib/jirametrics/status.rb +1 -1
- data/lib/jirametrics/stitcher.rb +81 -0
- data/lib/jirametrics/throughput_by_completed_resolution_chart.rb +22 -0
- data/lib/jirametrics/throughput_chart.rb +73 -23
- data/lib/jirametrics/time_based_histogram.rb +139 -0
- data/lib/jirametrics/time_based_scatterplot.rb +107 -0
- data/lib/jirametrics/wip_by_column_chart.rb +236 -0
- data/lib/jirametrics.rb +83 -69
- metadata +60 -6
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<%= seam_start %>
|
|
1
2
|
<div class="chart">
|
|
2
3
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
3
4
|
</div>
|
|
@@ -19,10 +20,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
19
20
|
display: true,
|
|
20
21
|
labelString: 'Days'
|
|
21
22
|
},
|
|
22
|
-
|
|
23
|
-
display: true,
|
|
24
|
-
text: 'Total time (days)'
|
|
25
|
-
},
|
|
23
|
+
<%= render_axis_title :x %>
|
|
26
24
|
grid: {
|
|
27
25
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
28
26
|
},
|
|
@@ -35,10 +33,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
35
33
|
min: 0,
|
|
36
34
|
max: <%= @highest_cycletime %>
|
|
37
35
|
},
|
|
38
|
-
|
|
39
|
-
display: true,
|
|
40
|
-
text: 'Time adding value (days)'
|
|
41
|
-
},
|
|
36
|
+
<%= render_axis_title :y %>
|
|
42
37
|
grid: {
|
|
43
38
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
44
39
|
},
|
|
@@ -83,3 +78,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
83
78
|
}
|
|
84
79
|
});
|
|
85
80
|
</script>
|
|
81
|
+
<%= seam_start %>
|
|
@@ -7,82 +7,98 @@
|
|
|
7
7
|
--cycletime-scatterplot-overall-trendline-color: gray;
|
|
8
8
|
|
|
9
9
|
--non-working-days-color: #F0F0F0;
|
|
10
|
-
--expedited-color:
|
|
11
|
-
--blocked-color: #
|
|
12
|
-
--stalled-color: orange
|
|
10
|
+
--expedited-color: #D55E00; /* Okabe-Ito vermilion */
|
|
11
|
+
--blocked-color: #D55E00; /* Okabe-Ito vermilion */
|
|
12
|
+
--stalled-color: #E69F00; /* Okabe-Ito orange */
|
|
13
13
|
--dead-color: black;
|
|
14
14
|
|
|
15
|
-
--type-story-color: #
|
|
16
|
-
--type-task-color: blue
|
|
17
|
-
--type-bug-color:
|
|
18
|
-
--type-spike-color: #
|
|
15
|
+
--type-story-color: #009E73; /* Okabe-Ito bluish green */
|
|
16
|
+
--type-task-color: #0072B2; /* Okabe-Ito blue */
|
|
17
|
+
--type-bug-color: #D55E00; /* Okabe-Ito vermilion */
|
|
18
|
+
--type-spike-color: #CC79A7; /* Okabe-Ito reddish purple */
|
|
19
19
|
|
|
20
20
|
--status-category-todo-color: gray;
|
|
21
|
-
--status-category-inprogress-color: #
|
|
22
|
-
--status-category-done-color: #
|
|
21
|
+
--status-category-inprogress-color: #0072B2; /* Okabe-Ito blue */
|
|
22
|
+
--status-category-done-color: #009E73; /* Okabe-Ito bluish green */
|
|
23
23
|
--status-category-unknown-color: black;
|
|
24
24
|
|
|
25
|
-
--aging-work-bar-chart-percentage-line-color:
|
|
25
|
+
--aging-work-bar-chart-percentage-line-color: #D55E00; /* Okabe-Ito vermilion */
|
|
26
26
|
--aging-work-bar-chart-separator-color: white;
|
|
27
27
|
|
|
28
28
|
--throughput_chart_total_line_color: gray;
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
--aging-work-in-progress-chart-shading-color: lightgray;
|
|
31
|
-
--aging-work-in-progress-chart-shading-50-color: #
|
|
32
|
-
--aging-work-in-progress-chart-shading-85-color: #
|
|
33
|
-
--aging-work-in-progress-chart-shading-98-color: #
|
|
34
|
-
--aging-work-in-progress-chart-shading-100-color: #
|
|
35
|
-
|
|
31
|
+
--aging-work-in-progress-chart-shading-50-color: #56B4E9; /* Okabe-Ito sky blue */
|
|
32
|
+
--aging-work-in-progress-chart-shading-85-color: #F0E442; /* Okabe-Ito yellow */
|
|
33
|
+
--aging-work-in-progress-chart-shading-98-color: #E69F00; /* Okabe-Ito orange */
|
|
34
|
+
--aging-work-in-progress-chart-shading-100-color: #D55E00; /* Okabe-Ito vermilion */
|
|
35
|
+
|
|
36
36
|
--aging-work-in-progress-by-age-trend-line-color: gray;
|
|
37
|
-
|
|
38
|
-
--aging-work-table-date-in-jeopardy: yellow
|
|
39
|
-
--aging-work-table-date-overdue:
|
|
37
|
+
|
|
38
|
+
--aging-work-table-date-in-jeopardy: #F0E442; /* Okabe-Ito yellow */
|
|
39
|
+
--aging-work-table-date-overdue: #D55E00; /* Okabe-Ito vermilion */
|
|
40
40
|
|
|
41
41
|
--hierarchy-table-inactive-item-text-color: gray;
|
|
42
42
|
|
|
43
|
-
--wip-chart-completed-color: #
|
|
44
|
-
--wip-chart-completed-but-not-started-color: #
|
|
45
|
-
--wip-chart-duration-less-than-day-color: #
|
|
46
|
-
--wip-chart-duration-week-or-less-color: #
|
|
47
|
-
--wip-chart-duration-two-weeks-or-less-color: #
|
|
48
|
-
--wip-chart-duration-four-weeks-or-less-color: #
|
|
49
|
-
--wip-chart-duration-more-than-four-weeks-color: #
|
|
50
|
-
--wip-chart-active-color: #
|
|
43
|
+
--wip-chart-completed-color: #009E73; /* Okabe-Ito bluish green */
|
|
44
|
+
--wip-chart-completed-but-not-started-color: #92D9C0; /* light bluish green */
|
|
45
|
+
--wip-chart-duration-less-than-day-color: #56B4E9; /* Okabe-Ito sky blue */
|
|
46
|
+
--wip-chart-duration-week-or-less-color: #F0E442; /* Okabe-Ito yellow */
|
|
47
|
+
--wip-chart-duration-two-weeks-or-less-color: #E69F00; /* Okabe-Ito orange */
|
|
48
|
+
--wip-chart-duration-four-weeks-or-less-color: #D55E00; /* Okabe-Ito vermilion */
|
|
49
|
+
--wip-chart-duration-more-than-four-weeks-color: #CC79A7; /* Okabe-Ito reddish purple */
|
|
50
|
+
--wip-chart-active-color: #0072B2; /* Okabe-Ito blue */
|
|
51
51
|
--wip-chart-border-color: gray;
|
|
52
52
|
|
|
53
|
-
--
|
|
54
|
-
--
|
|
55
|
-
--
|
|
56
|
-
--
|
|
53
|
+
--wip-by-column-chart-bar-fill-color: #0072B2; /* Okabe-Ito blue */
|
|
54
|
+
--wip-by-column-chart-bar-text-color: #ffffff;
|
|
55
|
+
--wip-by-column-chart-limit-line-color: #D55E00; /* Okabe-Ito vermilion */
|
|
56
|
+
--wip-by-column-chart-recommendation-color: #009E73; /* Okabe-Ito bluish green */
|
|
57
|
+
|
|
58
|
+
--estimate-accuracy-chart-completed-fill-color: #92D9C0; /* light bluish green */
|
|
59
|
+
--estimate-accuracy-chart-completed-border-color: #009E73; /* Okabe-Ito bluish green */
|
|
60
|
+
--estimate-accuracy-chart-active-fill-color: #F4C6AD; /* light vermilion */
|
|
61
|
+
--estimate-accuracy-chart-active-border-color: #D55E00; /* Okabe-Ito vermilion */
|
|
57
62
|
|
|
58
63
|
--expedited-chart-no-longer-expedited: gray;
|
|
59
|
-
--expedited-chart-dot-issue-started-color: orange
|
|
60
|
-
--expedited-chart-dot-issue-stopped-color: green
|
|
61
|
-
--expedited-chart-dot-expedite-started-color:
|
|
62
|
-
--expedited-chart-dot-expedite-stopped-color: green
|
|
64
|
+
--expedited-chart-dot-issue-started-color: #E69F00; /* Okabe-Ito orange */
|
|
65
|
+
--expedited-chart-dot-issue-stopped-color: #009E73; /* Okabe-Ito bluish green */
|
|
66
|
+
--expedited-chart-dot-expedite-started-color: #D55E00; /* Okabe-Ito vermilion */
|
|
67
|
+
--expedited-chart-dot-expedite-stopped-color: #009E73; /* Okabe-Ito bluish green */
|
|
63
68
|
|
|
64
|
-
--sprint-burndown-sprint-color-1: blue
|
|
65
|
-
--sprint-burndown-sprint-color-2: orange
|
|
66
|
-
--sprint-burndown-sprint-color-3: green
|
|
67
|
-
--sprint-burndown-sprint-color-4:
|
|
68
|
-
--sprint-burndown-sprint-color-5:
|
|
69
|
+
--sprint-burndown-sprint-color-1: #0072B2; /* Okabe-Ito blue */
|
|
70
|
+
--sprint-burndown-sprint-color-2: #E69F00; /* Okabe-Ito orange */
|
|
71
|
+
--sprint-burndown-sprint-color-3: #009E73; /* Okabe-Ito bluish green */
|
|
72
|
+
--sprint-burndown-sprint-color-4: #D55E00; /* Okabe-Ito vermilion */
|
|
73
|
+
--sprint-burndown-sprint-color-5: #CC79A7; /* Okabe-Ito reddish purple */
|
|
74
|
+
--sprint-burndown-sprint-color-6: #56B4E9; /* Okabe-Ito sky blue */
|
|
75
|
+
--sprint-burndown-sprint-color-7: #F0E442; /* Okabe-Ito yellow */
|
|
69
76
|
|
|
70
|
-
--
|
|
71
|
-
--daily-view-issue-border: green;
|
|
72
|
-
--daily-view-selected-issue-border: red;
|
|
77
|
+
--sprint-color: #56B4E9; /* Okabe-Ito sky blue */
|
|
73
78
|
|
|
79
|
+
--daily-view-selected-issue-background: lightgray;
|
|
80
|
+
--daily-view-issue-border: #009E73; /* Okabe-Ito bluish green */
|
|
81
|
+
--daily-view-selected-issue-border: #D55E00; /* Okabe-Ito vermilion */
|
|
82
|
+
|
|
83
|
+
/* The first five are the standard priorities that Jira creates by default. */
|
|
84
|
+
--priority-color-highest: #dc2626; /* red-600 - urgent red */
|
|
85
|
+
--priority-color-high: #ea580c; /* orange-600 - warning orange */
|
|
86
|
+
--priority-color-medium: #9ca3af; /* gray-400 - neutral light gray */
|
|
87
|
+
--priority-color-low: #0891b2; /* cyan-600 - calm blue */
|
|
88
|
+
--priority-color-lowest: #64748b; /* slate-500 - muted slate */
|
|
89
|
+
/* Then here are some values we've seen in multiple instances. */
|
|
90
|
+
--priority-color-notset: gray;
|
|
91
|
+
--priority-color-critical: red;
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
body {
|
|
77
95
|
background-color: var(--body-background);
|
|
78
96
|
color: var(--default-text-color);
|
|
97
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
98
|
+
font-size: 14px;
|
|
99
|
+
line-height: 1.5;
|
|
79
100
|
}
|
|
80
101
|
|
|
81
|
-
h1 {
|
|
82
|
-
border: 1px solid black;
|
|
83
|
-
background: lightgray;
|
|
84
|
-
padding-left: 0.2em;
|
|
85
|
-
}
|
|
86
102
|
dl, dd, dt {
|
|
87
103
|
padding: 0;
|
|
88
104
|
margin: 0;
|
|
@@ -191,6 +207,11 @@ div.daily_issue {
|
|
|
191
207
|
padding-right: 0.2em;
|
|
192
208
|
border-radius: 0.2em;
|
|
193
209
|
}
|
|
210
|
+
h1 {
|
|
211
|
+
border: none;
|
|
212
|
+
background: none;
|
|
213
|
+
padding-left: 0;
|
|
214
|
+
}
|
|
194
215
|
margin-bottom: 0.5em;
|
|
195
216
|
}
|
|
196
217
|
div.child_issue:hover {
|
|
@@ -203,45 +224,204 @@ div.child_issue {
|
|
|
203
224
|
padding: 0.5em;
|
|
204
225
|
}
|
|
205
226
|
|
|
227
|
+
/* Dark CSS variables — shared by the media query and the forced dark theme */
|
|
228
|
+
html[data-theme="dark"] {
|
|
229
|
+
--warning-banner: #9F2B00;
|
|
230
|
+
--non-working-days-color: #2f2f2f;
|
|
231
|
+
--type-story-color: #2DCB9A; /* lighter bluish green for dark bg */
|
|
232
|
+
--type-task-color: #56B4E9; /* sky blue for dark bg */
|
|
233
|
+
--type-bug-color: #E69F00; /* orange instead of vermilion for dark bg */
|
|
234
|
+
--body-background: #343434;
|
|
235
|
+
--default-text-color: #aaa;
|
|
236
|
+
--grid-line-color: #424242;
|
|
237
|
+
--expedited-color: #E69F00; /* Okabe-Ito orange for dark bg */
|
|
238
|
+
--blocked-color: #E69F00; /* Okabe-Ito orange for dark bg */
|
|
239
|
+
--stalled-color: #F0E442; /* Okabe-Ito yellow — distinct from blocked */
|
|
240
|
+
--wip-chart-active-color: #56B4E9; /* sky blue for dark bg */
|
|
241
|
+
--status-category-inprogress-color: #56B4E9; /* sky blue for dark bg */
|
|
242
|
+
--status-category-done-color: #2DCB9A; /* lighter bluish green for dark bg */
|
|
243
|
+
--hierarchy-table-inactive-item-text-color: #939393;
|
|
244
|
+
--wip-by-column-chart-bar-fill-color: #56B4E9; /* Okabe-Ito sky blue */
|
|
245
|
+
--wip-by-column-chart-bar-text-color: #000000;
|
|
246
|
+
--wip-by-column-chart-limit-line-color: #E69F00; /* Okabe-Ito orange */
|
|
247
|
+
--wip-by-column-chart-recommendation-color: #2DCB9A; /* lighter bluish green for dark bg */
|
|
248
|
+
--wip-chart-completed-color: #2DCB9A; /* lighter bluish green */
|
|
249
|
+
--wip-chart-duration-more-than-four-weeks-color: #DE9AC4; /* lighter reddish purple */
|
|
250
|
+
--estimate-accuracy-chart-completed-border-color: #2DCB9A;
|
|
251
|
+
--estimate-accuracy-chart-active-border-color: #E69F00;
|
|
252
|
+
--expedited-chart-dot-issue-stopped-color: #2DCB9A;
|
|
253
|
+
--expedited-chart-dot-expedite-started-color: #E69F00;
|
|
254
|
+
--expedited-chart-dot-expedite-stopped-color: #2DCB9A;
|
|
255
|
+
--sprint-burndown-sprint-color-1: #56B4E9; /* sky blue */
|
|
256
|
+
--sprint-burndown-sprint-color-3: #2DCB9A; /* lighter bluish green */
|
|
257
|
+
--sprint-burndown-sprint-color-4: #CC79A7; /* reddish purple (vermilion → orange conflicts with color-2) */
|
|
258
|
+
--sprint-burndown-sprint-color-5: #F0E442; /* yellow */
|
|
259
|
+
--sprint-burndown-sprint-color-6: #D55E00; /* vermilion (sky blue conflicts with color-1) */
|
|
260
|
+
--sprint-burndown-sprint-color-7: #92D9C0; /* light teal (yellow conflicts with color-5) */
|
|
261
|
+
--daily-view-selected-issue-background: #474747;
|
|
262
|
+
--daily-view-issue-border: #2DCB9A;
|
|
263
|
+
--daily-view-selected-issue-border: #E69F00;
|
|
264
|
+
--priority-color-highest: #ef4444;
|
|
265
|
+
--priority-color-high: #f97316;
|
|
266
|
+
--priority-color-low: #06b6d4;
|
|
267
|
+
--priority-color-lowest: #94a3b8;
|
|
268
|
+
|
|
269
|
+
a[href] { color: #1e8ad6; }
|
|
270
|
+
a[href]:hover { color: #3ba0e6; }
|
|
271
|
+
.chart { background: var(--body-background); }
|
|
272
|
+
table.standard {
|
|
273
|
+
th { background: var(--body-background); }
|
|
274
|
+
tr:nth-child(odd) { background-color: #656565; }
|
|
275
|
+
}
|
|
276
|
+
div.color_block { border: 1px solid lightgray; }
|
|
277
|
+
div.daily_issue {
|
|
278
|
+
.field { color: var(--default-text-color); }
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Force light mode — overrides the dark media query when user explicitly picks light */
|
|
283
|
+
html[data-theme="light"] {
|
|
284
|
+
--warning-banner: yellow;
|
|
285
|
+
--non-working-days-color: #F0F0F0;
|
|
286
|
+
--type-story-color: #009E73; /* Okabe-Ito bluish green */
|
|
287
|
+
--type-task-color: #0072B2; /* Okabe-Ito blue */
|
|
288
|
+
--type-bug-color: #D55E00; /* Okabe-Ito vermilion */
|
|
289
|
+
--body-background: white;
|
|
290
|
+
--default-text-color: black;
|
|
291
|
+
--grid-line-color: lightgray;
|
|
292
|
+
--expedited-color: #D55E00; /* Okabe-Ito vermilion */
|
|
293
|
+
--blocked-color: #D55E00; /* Okabe-Ito vermilion */
|
|
294
|
+
--stalled-color: #E69F00; /* Okabe-Ito orange */
|
|
295
|
+
--wip-chart-active-color: #0072B2; /* Okabe-Ito blue */
|
|
296
|
+
--status-category-inprogress-color: #0072B2; /* Okabe-Ito blue */
|
|
297
|
+
--status-category-done-color: #009E73; /* Okabe-Ito bluish green */
|
|
298
|
+
--hierarchy-table-inactive-item-text-color: gray;
|
|
299
|
+
--wip-by-column-chart-bar-fill-color: #0072B2;
|
|
300
|
+
--wip-by-column-chart-bar-text-color: #ffffff;
|
|
301
|
+
--wip-by-column-chart-limit-line-color: #D55E00;
|
|
302
|
+
--wip-by-column-chart-recommendation-color: #009E73;
|
|
303
|
+
--wip-chart-completed-color: #009E73; /* Okabe-Ito bluish green */
|
|
304
|
+
--wip-chart-completed-but-not-started-color: #92D9C0;
|
|
305
|
+
--wip-chart-duration-less-than-day-color: #56B4E9;
|
|
306
|
+
--wip-chart-duration-week-or-less-color: #F0E442;
|
|
307
|
+
--wip-chart-duration-two-weeks-or-less-color: #E69F00;
|
|
308
|
+
--wip-chart-duration-four-weeks-or-less-color: #D55E00;
|
|
309
|
+
--wip-chart-duration-more-than-four-weeks-color: #CC79A7;
|
|
310
|
+
--daily-view-selected-issue-background: lightgray;
|
|
311
|
+
--daily-view-issue-border: #009E73;
|
|
312
|
+
--daily-view-selected-issue-border: #D55E00;
|
|
313
|
+
--priority-color-highest: #dc2626;
|
|
314
|
+
--priority-color-high: #ea580c;
|
|
315
|
+
--priority-color-low: #0891b2;
|
|
316
|
+
--priority-color-lowest: #64748b;
|
|
317
|
+
|
|
318
|
+
a[href] { color: revert; }
|
|
319
|
+
a[href]:hover { color: revert; }
|
|
320
|
+
.chart { background: white; }
|
|
321
|
+
table.standard {
|
|
322
|
+
th { background: white; }
|
|
323
|
+
tr:nth-child(odd) { background-color: #eee; }
|
|
324
|
+
}
|
|
325
|
+
div.color_block { border: 1px solid black; }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Theme toggle widget */
|
|
329
|
+
#theme-toggle {
|
|
330
|
+
position: fixed;
|
|
331
|
+
top: 0.5rem;
|
|
332
|
+
right: 0.5rem;
|
|
333
|
+
display: flex;
|
|
334
|
+
gap: 2px;
|
|
335
|
+
background: var(--body-background);
|
|
336
|
+
border: 1px solid var(--grid-line-color);
|
|
337
|
+
border-radius: 6px;
|
|
338
|
+
padding: 3px;
|
|
339
|
+
z-index: 1000;
|
|
340
|
+
|
|
341
|
+
button {
|
|
342
|
+
background: none;
|
|
343
|
+
border: none;
|
|
344
|
+
cursor: pointer;
|
|
345
|
+
padding: 2px 6px;
|
|
346
|
+
border-radius: 4px;
|
|
347
|
+
font-size: 1rem;
|
|
348
|
+
color: var(--default-text-color);
|
|
349
|
+
opacity: 0.5;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
button:hover {
|
|
353
|
+
opacity: 1;
|
|
354
|
+
background: var(--grid-line-color);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
button.active {
|
|
358
|
+
opacity: 1;
|
|
359
|
+
background: var(--grid-line-color);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
206
363
|
@media screen and (prefers-color-scheme: dark) {
|
|
207
364
|
:root {
|
|
208
365
|
--warning-banner: #9F2B00;
|
|
209
366
|
|
|
210
367
|
--non-working-days-color: #2f2f2f;
|
|
211
|
-
--type-story-color: #
|
|
212
|
-
--type-task-color: #
|
|
213
|
-
--type-bug-color: #
|
|
368
|
+
--type-story-color: #2DCB9A; /* lighter bluish green for dark bg */
|
|
369
|
+
--type-task-color: #56B4E9; /* sky blue for dark bg */
|
|
370
|
+
--type-bug-color: #E69F00; /* orange instead of vermilion for dark bg */
|
|
214
371
|
|
|
215
372
|
--body-background: #343434;
|
|
216
373
|
--default-text-color: #aaa;
|
|
217
374
|
--grid-line-color: #424242;
|
|
218
375
|
|
|
219
|
-
--expedited-color: #
|
|
220
|
-
--blocked-color: #
|
|
221
|
-
--stalled-color: #
|
|
376
|
+
--expedited-color: #E69F00; /* Okabe-Ito orange for dark bg */
|
|
377
|
+
--blocked-color: #E69F00; /* Okabe-Ito orange for dark bg */
|
|
378
|
+
--stalled-color: #F0E442; /* Okabe-Ito yellow — distinct from blocked */
|
|
222
379
|
--dead-color: black;
|
|
223
|
-
--wip-chart-active-color: #
|
|
380
|
+
--wip-chart-active-color: #56B4E9; /* sky blue for dark bg */
|
|
381
|
+
|
|
382
|
+
--status-category-inprogress-color: #56B4E9; /* sky blue for dark bg */
|
|
383
|
+
--status-category-done-color: #2DCB9A; /* lighter bluish green for dark bg */
|
|
224
384
|
|
|
225
|
-
--
|
|
385
|
+
--wip-by-column-chart-bar-fill-color: #56B4E9;
|
|
386
|
+
--wip-by-column-chart-bar-text-color: #000000;
|
|
387
|
+
--wip-by-column-chart-limit-line-color: #E69F00;
|
|
388
|
+
--wip-by-column-chart-recommendation-color: #2DCB9A;
|
|
226
389
|
|
|
227
390
|
--cycletime-scatterplot-overall-trendline-color: gray;
|
|
228
391
|
|
|
229
392
|
--hierarchy-table-inactive-item-text-color: #939393;
|
|
230
393
|
|
|
231
|
-
--wip-chart-completed-color: #
|
|
232
|
-
--wip-chart-completed-but-not-started-color: #
|
|
233
|
-
--wip-chart-duration-less-than-day-color: #
|
|
234
|
-
--wip-chart-duration-week-or-less-color: #
|
|
235
|
-
--wip-chart-duration-two-weeks-or-less-color: #
|
|
236
|
-
--wip-chart-duration-four-weeks-or-less-color: #
|
|
237
|
-
--wip-chart-duration-more-than-four-weeks-color: #
|
|
394
|
+
--wip-chart-completed-color: #2DCB9A; /* lighter bluish green */
|
|
395
|
+
--wip-chart-completed-but-not-started-color: #92D9C0;
|
|
396
|
+
--wip-chart-duration-less-than-day-color: #56B4E9;
|
|
397
|
+
--wip-chart-duration-week-or-less-color: #F0E442;
|
|
398
|
+
--wip-chart-duration-two-weeks-or-less-color: #E69F00;
|
|
399
|
+
--wip-chart-duration-four-weeks-or-less-color: #D55E00;
|
|
400
|
+
--wip-chart-duration-more-than-four-weeks-color: #DE9AC4; /* lighter reddish purple */
|
|
238
401
|
|
|
239
|
-
--
|
|
240
|
-
|
|
402
|
+
--estimate-accuracy-chart-completed-border-color: #2DCB9A;
|
|
403
|
+
--estimate-accuracy-chart-active-border-color: #E69F00;
|
|
241
404
|
|
|
242
|
-
|
|
243
|
-
color: #
|
|
244
|
-
|
|
405
|
+
--expedited-chart-dot-issue-stopped-color: #2DCB9A;
|
|
406
|
+
--expedited-chart-dot-expedite-started-color: #E69F00;
|
|
407
|
+
--expedited-chart-dot-expedite-stopped-color: #2DCB9A;
|
|
408
|
+
|
|
409
|
+
--sprint-burndown-sprint-color-1: #56B4E9; /* sky blue */
|
|
410
|
+
--sprint-burndown-sprint-color-3: #2DCB9A; /* lighter bluish green */
|
|
411
|
+
--sprint-burndown-sprint-color-4: #CC79A7; /* reddish purple (vermilion → orange conflicts with color-2) */
|
|
412
|
+
--sprint-burndown-sprint-color-5: #F0E442; /* yellow */
|
|
413
|
+
--sprint-burndown-sprint-color-6: #D55E00; /* vermilion (sky blue conflicts with color-1) */
|
|
414
|
+
--sprint-burndown-sprint-color-7: #92D9C0; /* light teal (yellow conflicts with color-5) */
|
|
415
|
+
|
|
416
|
+
--daily-view-selected-issue-background: #474747;
|
|
417
|
+
--daily-view-issue-border: #2DCB9A;
|
|
418
|
+
--daily-view-selected-issue-border: #E69F00;
|
|
419
|
+
|
|
420
|
+
--priority-color-highest: #ef4444; /* red-500 - bright urgent red */
|
|
421
|
+
--priority-color-high: #f97316; /* orange-500 - bright orange */
|
|
422
|
+
--priority-color-medium: #9ca3af; /* gray-400 - neutral light gray */
|
|
423
|
+
--priority-color-low: #06b6d4; /* cyan-500 - bright calm blue */
|
|
424
|
+
--priority-color-lowest: #94a3b8; /* slate-400 - muted light slate */
|
|
245
425
|
}
|
|
246
426
|
|
|
247
427
|
a[href] {
|
|
@@ -277,4 +457,4 @@ div.child_issue {
|
|
|
277
457
|
}
|
|
278
458
|
}
|
|
279
459
|
|
|
280
|
-
}
|
|
460
|
+
}
|
|
@@ -1,45 +1,14 @@
|
|
|
1
1
|
<html>
|
|
2
2
|
<head>
|
|
3
3
|
<meta charset="UTF-8">
|
|
4
|
+
<title><%= project_name.empty? ? 'JiraMetrics' : "JiraMetrics - #{project_name}" %></title>
|
|
4
5
|
<link rel="icon" type="image/png" href="https://github.com/mikebowler/jirametrics/blob/main/favicon.png?raw=true" />
|
|
5
6
|
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.js"></script>
|
|
6
7
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
7
8
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script>
|
|
8
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/1.
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/3.1.0/chartjs-plugin-annotation.min.js"></script>
|
|
9
10
|
<script type="text/javascript">
|
|
10
|
-
|
|
11
|
-
link_text = document.getElementById(link_id).textContent
|
|
12
|
-
if( link_text == 'Show details') {
|
|
13
|
-
document.getElementById(link_id).textContent = 'Hide details'
|
|
14
|
-
document.getElementById(issues_id).style.display = 'block'
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
document.getElementById(link_id).textContent = 'Show details'
|
|
18
|
-
document.getElementById(issues_id).style.display = 'none'
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function toggle_visibility(open_link_id, close_link_id, toggleable_id) {
|
|
23
|
-
let open_link = document.getElementById(open_link_id)
|
|
24
|
-
let close_link = document.getElementById(close_link_id)
|
|
25
|
-
let toggleable_element = document.getElementById(toggleable_id)
|
|
26
|
-
|
|
27
|
-
if(open_link.style.display == 'none') {
|
|
28
|
-
open_link.style.display = 'block'
|
|
29
|
-
close_link.style.display = 'none'
|
|
30
|
-
toggleable_element.style.display = 'none'
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
open_link.style.display = 'none'
|
|
34
|
-
close_link.style.display = 'block'
|
|
35
|
-
toggleable_element.style.display = 'block'
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// If we switch between light/dark mode then force a refresh so all charts will redraw correctly
|
|
39
|
-
// in the other colour scheme.
|
|
40
|
-
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
41
|
-
location.reload()
|
|
42
|
-
})
|
|
11
|
+
<%= javascript %>
|
|
43
12
|
</script>
|
|
44
13
|
<style>
|
|
45
14
|
<%= css %>
|
|
@@ -49,12 +18,17 @@
|
|
|
49
18
|
</script>
|
|
50
19
|
</head>
|
|
51
20
|
<body>
|
|
21
|
+
<div id="theme-toggle">
|
|
22
|
+
<button id="theme-btn-system" title="Use system preference">⊙</button>
|
|
23
|
+
<button id="theme-btn-light" title="Light mode">☀</button>
|
|
24
|
+
<button id="theme-btn-dark" title="Dark mode">☾</button>
|
|
25
|
+
</div>
|
|
52
26
|
<noscript>
|
|
53
27
|
<div style="padding: 1em; background: red; color: white; font-size: 2em;">
|
|
54
28
|
Javascript is currently disabled and that means that almost all of the charts in this report won't render. If you've loaded this from a folder on SharePoint then save it locally and load it again.
|
|
55
29
|
</div>
|
|
56
30
|
</noscript>
|
|
57
31
|
<%= "\n" + @sections.collect { |text, type| text if type == :header }.compact.join("\n\n") %>
|
|
58
|
-
<%= "\n" + @sections.collect { |text, type| text if type
|
|
32
|
+
<%= "\n" + @sections.collect { |text, type| text if type != :header }.compact.join("\n\n") %>
|
|
59
33
|
</body>
|
|
60
34
|
</html>
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Apply saved theme immediately (before Chart.js reads CSS variables) so charts
|
|
2
|
+
// initialize with the correct colour scheme.
|
|
3
|
+
(function () {
|
|
4
|
+
const saved = localStorage.getItem('jirametrics:theme');
|
|
5
|
+
if (saved) {
|
|
6
|
+
document.documentElement.setAttribute('data-theme', saved);
|
|
7
|
+
}
|
|
8
|
+
}());
|
|
9
|
+
|
|
10
|
+
function makeFoldable() {
|
|
11
|
+
// Get all elements with the "foldable" class
|
|
12
|
+
const foldableElements = document.querySelectorAll('.foldable');
|
|
13
|
+
|
|
14
|
+
if (foldableElements.length === 0) {
|
|
15
|
+
return; // No foldable elements found
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Process each foldable element
|
|
19
|
+
foldableElements.forEach((element, index) => {
|
|
20
|
+
// Skip if this is the footer element
|
|
21
|
+
if (element.id === 'footer') {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create a unique ID for this section
|
|
26
|
+
const sectionId = `foldable-section-${index}`;
|
|
27
|
+
const toggleId = `foldable-toggle-${index}`;
|
|
28
|
+
|
|
29
|
+
// Create a container div for the foldable element and its content
|
|
30
|
+
const container = document.createElement('div');
|
|
31
|
+
container.className = 'foldable-section';
|
|
32
|
+
container.id = sectionId;
|
|
33
|
+
|
|
34
|
+
// Create a toggle button
|
|
35
|
+
const toggleButton = document.createElement(element.tagName); //'button');
|
|
36
|
+
toggleButton.id = toggleId;
|
|
37
|
+
toggleButton.className = 'foldable-toggle-btn';
|
|
38
|
+
toggleButton.innerHTML = '▼ ' + element.innerHTML;
|
|
39
|
+
|
|
40
|
+
// Create a content container
|
|
41
|
+
const contentContainer = document.createElement('div');
|
|
42
|
+
contentContainer.className = 'foldable-content';
|
|
43
|
+
contentContainer.style.cssText = `
|
|
44
|
+
border-left: 2px solid #ccc;
|
|
45
|
+
padding-left: 15px;
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
// Move the foldable element into the container and replace it with the toggle button
|
|
49
|
+
element.parentNode.insertBefore(container, element);
|
|
50
|
+
container.appendChild(toggleButton);
|
|
51
|
+
container.appendChild(contentContainer);
|
|
52
|
+
|
|
53
|
+
// Move all elements between this foldable element and the next foldable element (or end of document) into the content container
|
|
54
|
+
let nextElement = element.nextElementSibling;
|
|
55
|
+
while (nextElement && !nextElement.classList.contains('foldable')) {
|
|
56
|
+
// Skip the footer element
|
|
57
|
+
if (nextElement.id === 'footer') {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const temp = nextElement.nextElementSibling;
|
|
62
|
+
contentContainer.appendChild(nextElement);
|
|
63
|
+
nextElement = temp;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Remove the original foldable element
|
|
67
|
+
element.remove();
|
|
68
|
+
|
|
69
|
+
// Add click event to toggle visibility
|
|
70
|
+
toggleButton.addEventListener('click', function() {
|
|
71
|
+
const content = this.nextElementSibling;
|
|
72
|
+
if (content.style.display === 'none') {
|
|
73
|
+
content.style.display = 'block';
|
|
74
|
+
this.innerHTML = '▼ ' + this.innerHTML.substring(2);
|
|
75
|
+
} else {
|
|
76
|
+
content.style.display = 'none';
|
|
77
|
+
this.innerHTML = '▶ ' + this.innerHTML.substring(2);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Initially show the content (you can change this to 'none' if you want sections collapsed by default)
|
|
82
|
+
contentContainer.style.display = 'block';
|
|
83
|
+
if(element.classList.contains('startFolded')) {
|
|
84
|
+
toggleButton.click();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function initThemeToggle() {
|
|
90
|
+
const html = document.documentElement;
|
|
91
|
+
const saved = localStorage.getItem('jirametrics:theme');
|
|
92
|
+
if (saved) {
|
|
93
|
+
html.setAttribute('data-theme', saved);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function updateActiveButton(theme) {
|
|
97
|
+
['system', 'light', 'dark'].forEach(t => {
|
|
98
|
+
const btn = document.getElementById(`theme-btn-${t}`);
|
|
99
|
+
if (btn) {
|
|
100
|
+
btn.classList.toggle('active', t === theme);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setTheme(theme) {
|
|
106
|
+
if (theme === 'system') {
|
|
107
|
+
html.removeAttribute('data-theme');
|
|
108
|
+
localStorage.removeItem('jirametrics:theme');
|
|
109
|
+
} else {
|
|
110
|
+
html.setAttribute('data-theme', theme);
|
|
111
|
+
localStorage.setItem('jirametrics:theme', theme);
|
|
112
|
+
}
|
|
113
|
+
updateActiveButton(theme);
|
|
114
|
+
location.reload();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
updateActiveButton(saved || 'system');
|
|
118
|
+
|
|
119
|
+
['system', 'light', 'dark'].forEach(theme => {
|
|
120
|
+
const btn = document.getElementById(`theme-btn-${theme}`);
|
|
121
|
+
if (btn) {
|
|
122
|
+
btn.addEventListener('click', () => setTheme(theme));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Auto-initialize when DOM is loaded
|
|
128
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
129
|
+
makeFoldable();
|
|
130
|
+
initThemeToggle();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
// If we switch between light/dark mode then force a refresh so all charts will redraw correctly
|
|
135
|
+
// in the other colour scheme. Skip reload if a manual theme override is set.
|
|
136
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
137
|
+
if (!document.documentElement.hasAttribute('data-theme')) {
|
|
138
|
+
location.reload();
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// Draw a diagonal pattern to highlight sections of a bar chart. Based on code found at:
|
|
143
|
+
// https://stackoverflow.com/questions/28569667/fill-chart-js-bar-chart-with-diagonal-stripes-or-other-patterns
|
|
144
|
+
function createDiagonalPattern(color = 'black') {
|
|
145
|
+
// create a 5x5 px canvas for the pattern's base shape
|
|
146
|
+
let shape = document.createElement('canvas')
|
|
147
|
+
shape.width = 5
|
|
148
|
+
shape.height = 5
|
|
149
|
+
// get the context for drawing
|
|
150
|
+
let c = shape.getContext('2d')
|
|
151
|
+
// draw 1st line of the shape
|
|
152
|
+
c.strokeStyle = color
|
|
153
|
+
c.beginPath()
|
|
154
|
+
c.moveTo(1, 0)
|
|
155
|
+
c.lineTo(5, 4)
|
|
156
|
+
c.stroke()
|
|
157
|
+
// draw 2nd line of the shape
|
|
158
|
+
c.beginPath()
|
|
159
|
+
c.moveTo(0, 4)
|
|
160
|
+
c.lineTo(1, 5)
|
|
161
|
+
c.stroke()
|
|
162
|
+
// create the pattern from the shape
|
|
163
|
+
return c.createPattern(shape, 'repeat')
|
|
164
|
+
}
|