jirametrics 2.25 → 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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/bin/jirametrics-mcp +5 -0
  3. data/lib/jirametrics/aging_work_bar_chart.rb +10 -8
  4. data/lib/jirametrics/aging_work_in_progress_chart.rb +43 -11
  5. data/lib/jirametrics/aging_work_table.rb +5 -2
  6. data/lib/jirametrics/board.rb +9 -1
  7. data/lib/jirametrics/cfd_data_builder.rb +5 -0
  8. data/lib/jirametrics/chart_base.rb +14 -2
  9. data/lib/jirametrics/cumulative_flow_diagram.rb +8 -0
  10. data/lib/jirametrics/cycletime_scatterplot.rb +4 -0
  11. data/lib/jirametrics/daily_view.rb +5 -4
  12. data/lib/jirametrics/data_quality_report.rb +3 -1
  13. data/lib/jirametrics/dependency_chart.rb +1 -1
  14. data/lib/jirametrics/downloader.rb +18 -7
  15. data/lib/jirametrics/downloader_for_cloud.rb +68 -22
  16. data/lib/jirametrics/downloader_for_data_center.rb +1 -1
  17. data/lib/jirametrics/examples/aggregated_project.rb +1 -1
  18. data/lib/jirametrics/examples/standard_project.rb +5 -2
  19. data/lib/jirametrics/exporter.rb +12 -1
  20. data/lib/jirametrics/file_config.rb +9 -11
  21. data/lib/jirametrics/file_system.rb +31 -2
  22. data/lib/jirametrics/flow_efficiency_scatterplot.rb +1 -1
  23. data/lib/jirametrics/github_gateway.rb +13 -4
  24. data/lib/jirametrics/groupable_issue_chart.rb +2 -0
  25. data/lib/jirametrics/grouping_rules.rb +5 -1
  26. data/lib/jirametrics/html/cumulative_flow_diagram.erb +7 -8
  27. data/lib/jirametrics/html/index.css +139 -88
  28. data/lib/jirametrics/html/index.erb +1 -0
  29. data/lib/jirametrics/html/index.js +1 -1
  30. data/lib/jirametrics/html/legacy_colors.css +174 -0
  31. data/lib/jirametrics/html/time_based_scatterplot.erb +8 -3
  32. data/lib/jirametrics/html/wip_by_column_chart.erb +250 -0
  33. data/lib/jirametrics/html_generator.rb +2 -1
  34. data/lib/jirametrics/html_report_config.rb +33 -27
  35. data/lib/jirametrics/issue.rb +99 -6
  36. data/lib/jirametrics/jira_gateway.rb +26 -7
  37. data/lib/jirametrics/mcp_server.rb +531 -0
  38. data/lib/jirametrics/project_config.rb +20 -1
  39. data/lib/jirametrics/pull_request_cycle_time_histogram.rb +2 -2
  40. data/lib/jirametrics/pull_request_cycle_time_scatterplot.rb +13 -6
  41. data/lib/jirametrics/sprint_burndown.rb +1 -1
  42. data/lib/jirametrics/stitcher.rb +5 -0
  43. data/lib/jirametrics/throughput_chart.rb +18 -2
  44. data/lib/jirametrics/time_based_scatterplot.rb +9 -2
  45. data/lib/jirametrics/wip_by_column_chart.rb +236 -0
  46. data/lib/jirametrics.rb +58 -0
  47. metadata +36 -2
@@ -65,22 +65,20 @@ class FileConfig
65
65
  # most common usecase - the Team Dashboard from FocusedObjective.com. The rule for that one
66
66
  # is that all empty values in the first column should be at the bottom.
67
67
  def sort_output all_lines
68
- all_lines.sort do |a, b|
69
- result = nil
70
- if a[0] == b[0]
71
- result = a[1..] <=> b[1..]
68
+ all_lines.each_with_index.sort do |(a, a_idx), (b, b_idx)|
69
+ result = if a[0] == b[0]
70
+ a[1..] <=> b[1..]
72
71
  elsif a[0].nil?
73
- result = 1
72
+ 1
74
73
  elsif b[0].nil?
75
- result = -1
74
+ -1
76
75
  else
77
- result = a[0] <=> b[0]
76
+ a[0] <=> b[0]
78
77
  end
79
78
 
80
- # This will only happen if one of the objects isn't comparable. Seen in production.
81
- result = -1 if result.nil?
82
- result
83
- end
79
+ # When objects aren't comparable, preserve original order for a stable sort.
80
+ result.nil? || result.zero? ? a_idx <=> b_idx : result
81
+ end.map(&:first)
84
82
  end
85
83
 
86
84
  def columns &block
@@ -3,13 +3,14 @@
3
3
  require 'json'
4
4
 
5
5
  class FileSystem
6
- attr_accessor :logfile, :logfile_name
6
+ attr_accessor :logfile, :logfile_name, :log_only
7
7
 
8
8
  def initialize
9
9
  # In almost all cases, this will be immediately replaced in the Exporter
10
10
  # but if we fail before we get that far, this will at least let a useful
11
11
  # error show up on the console.
12
12
  @logfile = $stdout
13
+ @log_only = false
13
14
  end
14
15
 
15
16
  # Effectively the same as File.read except it forces the encoding to UTF-8
@@ -59,7 +60,7 @@ class FileSystem
59
60
 
60
61
  logfile.puts message
61
62
  logfile.puts more if more
62
- return unless also_write_to_stderr
63
+ return if log_only || !also_write_to_stderr
63
64
 
64
65
  # Obscure edge-case where we're trying to log something before logging is even
65
66
  # set up. Quick escape here so that we don't dump the error twice.
@@ -68,6 +69,34 @@ class FileSystem
68
69
  $stderr.puts message # rubocop:disable Style/StderrPuts
69
70
  end
70
71
 
72
+ def log_start message
73
+ logfile.puts message
74
+ return if log_only || logfile == $stdout
75
+
76
+ $stderr.print message
77
+ $stderr.flush
78
+ end
79
+
80
+ def start_progress
81
+ return if log_only
82
+
83
+ $stderr.print ' '
84
+ $stderr.flush
85
+ end
86
+
87
+ def progress_dot
88
+ return if log_only
89
+
90
+ $stderr.print '.'
91
+ $stderr.flush
92
+ end
93
+
94
+ def end_progress
95
+ return if log_only
96
+
97
+ $stderr.puts '' # rubocop:disable Style/StderrPuts
98
+ end
99
+
71
100
  # In some Jira instances, a sizeable portion of the JSON is made up of empty fields. I've seen
72
101
  # cases where this simple compression will drop the filesize by half.
73
102
  def compress node
@@ -63,7 +63,7 @@ class FlowEfficiencyScatterplot < ChartBase
63
63
  end
64
64
 
65
65
  if data_sets.empty?
66
- return "<h1 class='foldable'>#{@header_text}</h1>No data matched the selected criteria. Nothing to show."
66
+ return "<h1 class='foldable'>#{@header_text}</h1><div>No data matched the selected criteria. Nothing to show.</div>"
67
67
  end
68
68
 
69
69
  wrap_and_render(binding, __FILE__)
@@ -64,9 +64,10 @@ class GithubGateway
64
64
  raw_pr['body']
65
65
  ]
66
66
 
67
- sources.compact
68
- .flat_map { |s| s.scan(@issue_key_pattern) }
69
- .uniq
67
+ keys = sources.compact.flat_map { |s| s.scan(@issue_key_pattern) }.uniq
68
+ return keys unless keys.empty?
69
+
70
+ commit_messages_for(raw_pr['number']).flat_map { |msg| msg.scan(@issue_key_pattern) }.uniq
70
71
  end
71
72
 
72
73
  def extract_reviews raw_reviews
@@ -83,11 +84,19 @@ class GithubGateway
83
84
 
84
85
  private
85
86
 
87
+ def commit_messages_for pr_number
88
+ args = ['pr', 'view', pr_number.to_s, '--json', 'commits', '--repo', @repo]
89
+ result = run_command(args)
90
+ (result['commits'] || []).flat_map do |commit|
91
+ [commit['messageHeadline'], commit['messageBody']].compact
92
+ end
93
+ end
94
+
86
95
  def build_issue_key_pattern
87
96
  return nil if @project_keys.empty?
88
97
 
89
98
  keys_pattern = @project_keys.map { |k| Regexp.escape(k) }.join('|')
90
- Regexp.new("\\b(?:#{keys_pattern})-\\d+\\b")
99
+ Regexp.new("\\b(?:#{keys_pattern})-\\d+(?![A-Za-z0-9])")
91
100
  end
92
101
 
93
102
  def run_command args
@@ -17,6 +17,7 @@ module GroupableIssueChart
17
17
  result = {}
18
18
  ignored_issues = []
19
19
  @issue_hints = {}
20
+ @issue_periods = {}
20
21
  completed_issues.each do |issue|
21
22
  rules = GroupingRules.new
22
23
  @group_by_block.call(issue, rules)
@@ -26,6 +27,7 @@ module GroupableIssueChart
26
27
  end
27
28
 
28
29
  @issue_hints[issue] = rules.issue_hint
30
+ @issue_periods[issue] = rules.last_day_of_period
29
31
  (result[rules] ||= []) << issue
30
32
  end
31
33
 
@@ -2,7 +2,11 @@
2
2
 
3
3
  class GroupingRules < Rules
4
4
  attr_accessor :label, :issue_hint, :label_hint
5
- attr_reader :color
5
+ attr_reader :color, :last_day_of_period
6
+
7
+ def last_day_of_period= value
8
+ @last_day_of_period = value.is_a?(String) ? Date.parse(value) : value
9
+ end
6
10
 
7
11
  def eql? other
8
12
  other.label == @label && other.color == @color
@@ -271,14 +271,6 @@ if (!Chart.Tooltip.positioners.legendItem) {
271
271
  ctx.moveTo(xA, yB);
272
272
  ctx.lineTo(xC, yA);
273
273
  ctx.stroke();
274
- } else {
275
- // C outside range: dashed extension to right edge
276
- ctx.setLineDash([4, 2]);
277
- ctx.lineWidth = 1.5;
278
- ctx.beginPath();
279
- ctx.moveTo(xA, yA);
280
- ctx.lineTo(ca.right, yA);
281
- ctx.stroke();
282
274
  }
283
275
 
284
276
  ctx.restore();
@@ -310,6 +302,12 @@ if (!Chart.Tooltip.positioners.legendItem) {
310
302
  id: 'cfdFlowMetrics',
311
303
 
312
304
  afterInit(chart) {
305
+ // Guard against being applied to non-CFD charts (Chart.js may fire
306
+ // inline plugin hooks on other charts when plugins share an id with
307
+ // a cached entry).
308
+ const ds = chart.data.datasets;
309
+ if (!ds || !ds[0] || !ds[0].data || !ds[0].data.length) return;
310
+
313
311
  const { arrivals, departures } = buildArrays(chart);
314
312
  const dates = chart.data.datasets[0].data.map(d => new Date(d.x).getTime());
315
313
  chart._flowMetrics = {
@@ -426,6 +424,7 @@ if (!Chart.Tooltip.positioners.legendItem) {
426
424
 
427
425
  afterDraw(chart) {
428
426
  const fm = chart._flowMetrics;
427
+ if (!fm) return;
429
428
  drawTrendLines(chart, fm);
430
429
  // Triangle is on the overlay canvas — no drawing needed here.
431
430
  }
@@ -7,71 +7,78 @@
7
7
  --cycletime-scatterplot-overall-trendline-color: gray;
8
8
 
9
9
  --non-working-days-color: #F0F0F0;
10
- --expedited-color: red;
11
- --blocked-color: #FF7400;
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: #4bc14b;
16
- --type-task-color: blue;
17
- --type-bug-color: orange;
18
- --type-spike-color: #9400D3;
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: #2663ff;
22
- --status-category-done-color: #00ff00;
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: red;
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: #2E8BC0; // green;
32
- --aging-work-in-progress-chart-shading-85-color: #ADD8E6; // yellow;
33
- --aging-work-in-progress-chart-shading-98-color: #FF8A8A; // orange;
34
- --aging-work-in-progress-chart-shading-100-color: #FF2E2E; // red;
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: red;
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: #00ff00;
44
- --wip-chart-completed-but-not-started-color: #99FF99;
45
- --wip-chart-duration-less-than-day-color: #ffef41;
46
- --wip-chart-duration-week-or-less-color: #dcc900;
47
- --wip-chart-duration-two-weeks-or-less-color: #dfa000;
48
- --wip-chart-duration-four-weeks-or-less-color: #eb7200;
49
- --wip-chart-duration-more-than-four-weeks-color: #e70000;
50
- --wip-chart-active-color: #326cff;
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
- --estimate-accuracy-chart-completed-fill-color: #00ff00;
54
- --estimate-accuracy-chart-completed-border-color: green;
55
- --estimate-accuracy-chart-active-fill-color: #FFCCCB;
56
- --estimate-accuracy-chart-active-border-color: red;
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: red;
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: red;
68
- --sprint-burndown-sprint-color-5: brown;
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
- --sprint-color: lightblue;
77
+ --sprint-color: #56B4E9; /* Okabe-Ito sky blue */
71
78
 
72
79
  --daily-view-selected-issue-background: lightgray;
73
- --daily-view-issue-border: green;
74
- --daily-view-selected-issue-border: red;
80
+ --daily-view-issue-border: #009E73; /* Okabe-Ito bluish green */
81
+ --daily-view-selected-issue-border: #D55E00; /* Okabe-Ito vermilion */
75
82
 
76
83
  /* The first five are the standard priorities that Jira creates by default. */
77
84
  --priority-color-highest: #dc2626; /* red-600 - urgent red */
@@ -221,25 +228,39 @@ div.child_issue {
221
228
  html[data-theme="dark"] {
222
229
  --warning-banner: #9F2B00;
223
230
  --non-working-days-color: #2f2f2f;
224
- --type-story-color: #6fb86f;
225
- --type-task-color: #0021b3;
226
- --type-bug-color: #bb5603;
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 */
227
234
  --body-background: #343434;
228
235
  --default-text-color: #aaa;
229
236
  --grid-line-color: #424242;
230
- --expedited-color: #b90000;
231
- --blocked-color: #c75b02;
232
- --stalled-color: #ae7202;
233
- --wip-chart-active-color: #2551c1;
234
- --status-category-inprogress-color: #1c49bb;
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 */
235
243
  --hierarchy-table-inactive-item-text-color: #939393;
236
- --wip-chart-completed-color: #03cb03;
237
- --wip-chart-duration-less-than-day-color: #d2d988;
238
- --wip-chart-duration-week-or-less-color: #dfcd00;
239
- --wip-chart-duration-two-weeks-or-less-color: #cf9400;
240
- --wip-chart-duration-four-weeks-or-less-color: #c25e00;
241
- --wip-chart-duration-more-than-four-weeks-color: #8e0000;
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) */
242
261
  --daily-view-selected-issue-background: #474747;
262
+ --daily-view-issue-border: #2DCB9A;
263
+ --daily-view-selected-issue-border: #E69F00;
243
264
  --priority-color-highest: #ef4444;
244
265
  --priority-color-high: #f97316;
245
266
  --priority-color-low: #06b6d4;
@@ -262,25 +283,33 @@ html[data-theme="dark"] {
262
283
  html[data-theme="light"] {
263
284
  --warning-banner: yellow;
264
285
  --non-working-days-color: #F0F0F0;
265
- --type-story-color: #4bc14b;
266
- --type-task-color: blue;
267
- --type-bug-color: orange;
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 */
268
289
  --body-background: white;
269
290
  --default-text-color: black;
270
291
  --grid-line-color: lightgray;
271
- --expedited-color: red;
272
- --blocked-color: #FF7400;
273
- --stalled-color: orange;
274
- --wip-chart-active-color: #326cff;
275
- --status-category-inprogress-color: #2663ff;
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 */
276
298
  --hierarchy-table-inactive-item-text-color: gray;
277
- --wip-chart-completed-color: #00ff00;
278
- --wip-chart-duration-less-than-day-color: #ffef41;
279
- --wip-chart-duration-week-or-less-color: #dcc900;
280
- --wip-chart-duration-two-weeks-or-less-color: #dfa000;
281
- --wip-chart-duration-four-weeks-or-less-color: #eb7200;
282
- --wip-chart-duration-more-than-four-weeks-color: #e70000;
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;
283
310
  --daily-view-selected-issue-background: lightgray;
311
+ --daily-view-issue-border: #009E73;
312
+ --daily-view-selected-issue-border: #D55E00;
284
313
  --priority-color-highest: #dc2626;
285
314
  --priority-color-high: #ea580c;
286
315
  --priority-color-low: #0891b2;
@@ -336,41 +365,63 @@ html[data-theme="light"] {
336
365
  --warning-banner: #9F2B00;
337
366
 
338
367
  --non-working-days-color: #2f2f2f;
339
- --type-story-color: #6fb86f;
340
- --type-task-color: #0021b3;
341
- --type-bug-color: #bb5603;
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 */
342
371
 
343
372
  --body-background: #343434;
344
373
  --default-text-color: #aaa;
345
374
  --grid-line-color: #424242;
346
375
 
347
- --expedited-color: #b90000;
348
- --blocked-color: #c75b02;
349
- --stalled-color: #ae7202;
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 */
350
379
  --dead-color: black;
351
- --wip-chart-active-color: #2551c1;
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 */
352
384
 
353
- --status-category-inprogress-color: #1c49bb;
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;
354
389
 
355
390
  --cycletime-scatterplot-overall-trendline-color: gray;
356
391
 
357
392
  --hierarchy-table-inactive-item-text-color: #939393;
358
393
 
359
- --wip-chart-completed-color: #03cb03;
360
- --wip-chart-completed-but-not-started-color: #99FF99;
361
- --wip-chart-duration-less-than-day-color: #d2d988;
362
- --wip-chart-duration-week-or-less-color: #dfcd00;
363
- --wip-chart-duration-two-weeks-or-less-color: #cf9400;
364
- --wip-chart-duration-four-weeks-or-less-color: #c25e00;
365
- --wip-chart-duration-more-than-four-weeks-color: #8e0000;
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 */
401
+
402
+ --estimate-accuracy-chart-completed-border-color: #2DCB9A;
403
+ --estimate-accuracy-chart-active-border-color: #E69F00;
404
+
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) */
366
415
 
367
416
  --daily-view-selected-issue-background: #474747;
417
+ --daily-view-issue-border: #2DCB9A;
418
+ --daily-view-selected-issue-border: #E69F00;
368
419
 
369
420
  --priority-color-highest: #ef4444; /* red-500 - bright urgent red */
370
421
  --priority-color-high: #f97316; /* orange-500 - bright orange */
371
422
  --priority-color-medium: #9ca3af; /* gray-400 - neutral light gray */
372
423
  --priority-color-low: #06b6d4; /* cyan-500 - bright calm blue */
373
- --priority-color-lowest: #94a3b8; /* slate-400 - muted light slate */
424
+ --priority-color-lowest: #94a3b8; /* slate-400 - muted light slate */
374
425
  }
375
426
 
376
427
  a[href] {
@@ -406,4 +457,4 @@ html[data-theme="light"] {
406
457
  }
407
458
  }
408
459
 
409
- }
460
+ }
@@ -1,6 +1,7 @@
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>
@@ -35,7 +35,7 @@ function makeFoldable() {
35
35
  const toggleButton = document.createElement(element.tagName); //'button');
36
36
  toggleButton.id = toggleId;
37
37
  toggleButton.className = 'foldable-toggle-btn';
38
- toggleButton.innerHTML = '▼ ' + element.textContent;
38
+ toggleButton.innerHTML = '▼ ' + element.innerHTML;
39
39
 
40
40
  // Create a content container
41
41
  const contentContainer = document.createElement('div');