kanaui 5.0.0 → 5.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b83ffa9a7fd38924effe4fa1d41bf931884e21db37d6e098931f864cd8e6f168
4
- data.tar.gz: 18548789c20e02693bfeec178331b27bc43d32c10473638ed3819253f861cb20
3
+ metadata.gz: a6d1a8dc2490699d189f74a1c0e895e29563a342eee594a0739d0dc5b47bafd4
4
+ data.tar.gz: 83b3fe017d28bb1c44c2ff1641ead0c64a2c8fab62607d533f415f2609f651ae
5
5
  SHA512:
6
- metadata.gz: 4d5f2cf67c9412dde6dbf0e74abb92557bd47b90180a5f9c6a41ec5a8d2a1911ed321e4429cc695a7ea4aa72c26b2c78db27d102b1bed1d54023658ecf46d22b
7
- data.tar.gz: 965d83c8c2ea050106c85e5568a282573c5b70cd1679e78c80683ed6a96ca1206bcda2dbe10cecd1df7d774387bc56a4071d01b7f5368a7fa06249914642585b
6
+ metadata.gz: fa06748ad56e379a637fcc1348af0fdbc922d0c0a3923ec88abeaf7d56b0994c5bcae3a5b02e354e11927e74015f7764a3ea334a568f831a633d98d9a64d1946
7
+ data.tar.gz: 0a89abd1fccd25272ece9b7c01a725f64f177d41574e35c7611f80f3ba57f49ab7411426231a4495425549d9684c873fdfd2029a77bb25e9ffebbe703e949f38
@@ -3,14 +3,12 @@
3
3
  var self = this;
4
4
 
5
5
  var makeXAxis = function () {
6
- return d3.svg.axis().scale(self.x).orient("bottom").ticks(6);
6
+ return d3.axisBottom(self.x).ticks(6);
7
7
  };
8
8
 
9
9
  var makeYAxis = function () {
10
- return d3.svg
11
- .axis()
12
- .scale(self.y)
13
- .orient("left")
10
+ return d3
11
+ .axisLeft(self.y)
14
12
  .tickFormat(d3.format(",d"));
15
13
  };
16
14
 
@@ -28,13 +26,13 @@
28
26
  "transform",
29
27
  "translate(" + self.margin_left + "," + self.height + ")"
30
28
  )
31
- .call(makeXAxis().tickSize(-self.height, 0, 0).tickFormat(""));
29
+ .call(makeXAxis().tickSize(-self.height).tickFormat(""));
32
30
 
33
31
  svg
34
32
  .append("g")
35
33
  .attr("class", "grid")
36
34
  .attr("transform", "translate(" + self.margin_left + ",0)")
37
- .call(makeYAxis().tickSize(-self.width, 0, 0).tickFormat(""));
35
+ .call(makeYAxis().tickSize(-self.width).tickFormat(""));
38
36
 
39
37
  svg
40
38
  .append("g")
@@ -2,10 +2,10 @@
2
2
  Kiddo.LineChart = function () {
3
3
  var self = this;
4
4
 
5
- this.x = d3.time.scale().range([0, this.width]);
6
- this.y = d3.scale.linear().range([this.height, 0]);
5
+ this.x = d3.scaleTime().range([0, this.width]);
6
+ this.y = d3.scaleLinear().range([this.height, 0]);
7
7
 
8
- var valueline = d3.svg
8
+ var valueline = d3
9
9
  .line()
10
10
  .x(function (d) {
11
11
  return self.x(d.x);
@@ -13,59 +13,60 @@
13
13
  .y(function (d) {
14
14
  return self.y(d.y);
15
15
  })
16
- .interpolate("monotone"); // Smooth line interpolation
16
+ .curve(d3.curveMonotoneX);
17
17
 
18
18
  var axes = Kiddo.Axes.apply(this);
19
19
  var helper = new Kiddo.Helper();
20
20
 
21
- // Custom blue color theme matching the image
22
- var blueColors = [
23
- "#1565C0",
24
- "#1976D2",
25
- "#2196F3",
26
- "#42A5F5",
27
- "#64B5F6",
28
- "#90CAF9",
29
- "#BBDEFB",
30
- "#E3F2FD",
21
+ // Custom multi-color theme matching the image
22
+ var colors = [
23
+ "#2196F3", // blue
24
+ "#E53935", // red
25
+ "#43A047", // green
26
+ "#FB8C00", // orange
27
+ "#8E24AA", // purple
28
+ "#00ACC1", // cyan
29
+ "#F4511E", // deep orange
30
+ "#6D4C41", // brown
31
31
  ];
32
- self.color = d3.scale.ordinal().range(blueColors);
32
+ self.color = d3.scaleOrdinal().range(colors);
33
33
 
34
34
  return {
35
35
  render: function (svg, json) {
36
36
  var title = json.title,
37
37
  datasets = json.data;
38
38
 
39
+ datasets.forEach(function (dataset) {
40
+ dataset.values.forEach(function (d) {
41
+ d.date = d.x.split("T")[0];
42
+ d.x = helper.parseDate(d.date);
43
+ d.y = +d.y;
44
+ });
45
+ });
46
+
39
47
  // Scale the range of the data before rendering axes
40
48
  var allValues = datasets.reduce(function (result, element) {
41
49
  return result.concat(element.values);
42
50
  }, []);
43
51
 
44
52
  var x_domain = d3.extent(allValues, function (d) {
45
- return new Date(d.x);
53
+ return d.x;
46
54
  });
47
55
 
48
56
  self.x.domain(x_domain);
49
57
 
50
- var y_domain = [
51
- d3.min(datasets, function (datum) {
52
- return d3.min(datum.values, function (d) {
53
- return d.y;
54
- });
55
- }),
56
- d3.max(datasets, function (datum) {
57
- return d3.max(datum.values, function (d) {
58
- return d.y;
59
- });
60
- }),
61
- ];
58
+ var y_domain = d3.extent(allValues, function (d) {
59
+ return d.y;
60
+ });
62
61
 
63
62
  self.y.domain(y_domain);
64
63
 
65
64
  // Render axes first
66
65
  axes.render(svg, title);
67
66
 
68
- self.color.domain(d3.keys(datasets));
67
+ self.color.domain(datasets.map(function (dataset) {
68
+ return dataset.name;
69
+ }));
69
70
 
70
71
  // Create legend container at the top
71
72
  var legendContainer = svg
@@ -108,7 +109,7 @@
108
109
 
109
110
  // Add text label
110
111
  var labelText =
111
- d.name + " :: " + d.count + ": " + d3.format(",.2f")(d.value);
112
+ helper.formatSeriesName(d.name) + " (" + d.count + "): " + d3.format(",.2f")(d.value);
112
113
  legendItem
113
114
  .append("text")
114
115
  .attr("x", xOffset + 18)
@@ -129,12 +130,6 @@
129
130
  var data = dataset.values,
130
131
  name = dataset.name;
131
132
 
132
- data.forEach(function (d) {
133
- d.date = d.x.split("T")[0]; // Support both date and date/times
134
- d.x = helper.parseDate(d.date);
135
- d.y = +d.y;
136
- });
137
-
138
133
  svg
139
134
  .append("path")
140
135
  .attr("class", "line")
@@ -2,26 +2,27 @@
2
2
  Kiddo.PieChart = function () {
3
3
  var self = this;
4
4
  var radius = Math.min(this.width, this.height) / 2;
5
+ var helper = new Kiddo.Helper();
5
6
 
6
7
  // Custom blue color theme - matching the line chart
7
- var blueColors = [
8
- "#1565C0",
9
- "#1976D2",
10
- "#2196F3",
11
- "#42A5F5",
12
- "#64B5F6",
13
- "#90CAF9",
14
- "#BBDEFB",
15
- "#E3F2FD",
8
+ var colors = [
9
+ "#2196F3", // blue
10
+ "#E53935", // red
11
+ "#43A047", // green
12
+ "#FB8C00", // orange
13
+ "#8E24AA", // purple
14
+ "#00ACC1", // cyan
15
+ "#F4511E", // deep orange
16
+ "#6D4C41", // brown
16
17
  ];
17
- var color = d3.scale.ordinal().range(blueColors);
18
+ var color = d3.scaleOrdinal().range(colors);
18
19
 
19
- var arc = d3.svg
20
+ var arc = d3
20
21
  .arc()
21
22
  .outerRadius(radius - 10)
22
23
  .innerRadius(0);
23
24
 
24
- var pie = d3.layout
25
+ var pie = d3
25
26
  .pie()
26
27
  .sort(null)
27
28
  .value(function (d) {
@@ -41,6 +42,10 @@
41
42
  d.value = +d.value;
42
43
  });
43
44
 
45
+ color.domain(data.map(function (d, index) {
46
+ return index;
47
+ }));
48
+
44
49
  var g = svg
45
50
  .selectAll(".arc")
46
51
  .data(pie(data))
@@ -74,7 +79,7 @@
74
79
  })
75
80
  .html(function (d, i) {
76
81
  return (
77
- colorCircle(d.data.value, i) + d.data.label + ": " + d.data.value
82
+ colorCircle(d.data.value, i) + helper.formatSeriesName(d.data.label) + ": " + d.data.value
78
83
  );
79
84
  })
80
85
  .attr("class", "chart_values");
@@ -80,8 +80,8 @@
80
80
  .attr("transform", "translate(" + self.margin_left + ",0)");
81
81
  });
82
82
 
83
- function mousemove() {
84
- var _this = this;
83
+ function mousemove(event) {
84
+ var mouseX = d3.pointer(event, this)[0];
85
85
 
86
86
  $("#mouseover_canvas .chart_values").detach().remove();
87
87
  $("#mouseover_canvas .chart_circles").detach().remove();
@@ -94,7 +94,7 @@
94
94
  var data = element.values;
95
95
  var name = element.name;
96
96
 
97
- var x0 = x.invert(d3.mouse(_this)[0]),
97
+ var x0 = x.invert(mouseX),
98
98
  i = helper.bisectDate(data, x0, 1),
99
99
  d0 = data[i - 1],
100
100
  d1 = data[i];
@@ -5,15 +5,58 @@
5
5
  };
6
6
  var formatValue = d3.format(",.2f");
7
7
 
8
+ var humanizeSegment = function (segment) {
9
+ segment = String(segment || "")
10
+ .replace(/[_-]+/g, " ")
11
+ .replace(/\s+/g, " ")
12
+ .trim();
13
+
14
+ if (!segment) {
15
+ return "";
16
+ }
17
+
18
+ return segment
19
+ .split(" ")
20
+ .map(function (word) {
21
+ if (/^\d+(\.\d+)?$/.test(word)) {
22
+ return word;
23
+ }
24
+ if (/^[A-Z]{2,3}$/.test(word)) {
25
+ return word;
26
+ }
27
+
28
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
29
+ })
30
+ .join(" ");
31
+ };
32
+
33
+ var formatSeriesName = function (name) {
34
+ return String(name || "")
35
+ .split(/\s*::\s*/)
36
+ .map(function (segment) {
37
+ var parts = segment.split(/\s*:\s*/);
38
+ if (parts.length > 1) {
39
+ return humanizeSegment(parts[0]) + " (" + humanizeSegment(parts.slice(1).join(": ")) + ")";
40
+ }
41
+
42
+ return humanizeSegment(segment);
43
+ })
44
+ .filter(function (segment) {
45
+ return segment.length > 0;
46
+ })
47
+ .join(" / ");
48
+ };
49
+
8
50
  return {
9
- parseDate: d3.time.format("%Y-%m-%d").parse,
51
+ parseDate: d3.timeParse("%Y-%m-%d"),
10
52
  bisectDate: d3.bisector(function (d) {
11
53
  return d.x;
12
54
  }).left,
13
55
  formatCurrency: formatCurrency,
14
56
  formatValue: formatValue,
57
+ formatSeriesName: formatSeriesName,
15
58
  formatValueDisplay: function (name, d) {
16
- return name + ": " + formatValue(d.y); // Add currency boolean on backend later -- formatCurrency(d.y); }
59
+ return formatSeriesName(name) + ": " + formatValue(d.y); // Add currency boolean on backend later -- formatCurrency(d.y); }
17
60
  },
18
61
  };
19
62
  };
@@ -1,19 +1,36 @@
1
1
  (function (d3, $, window, document, undefined) {
2
+ function errorMessage(error) {
3
+ if (error && error.responseText) {
4
+ try {
5
+ var response = JSON.parse(error.responseText);
6
+ if (response.message) {
7
+ return response.message;
8
+ }
9
+ } catch (ex) {
10
+ return error.responseText;
11
+ }
12
+ }
13
+
14
+ return error && error.message ? error.message : String(error);
15
+ }
16
+
17
+ function renderError(message) {
18
+ var escapedMessage = $("<div/>").text(message).html();
19
+ $("#chartAnchor").prepend(
20
+ '<div class="alert alert-danger" role="alert">' + escapedMessage + "</div>"
21
+ );
22
+ }
23
+
2
24
  $(document).ready(function () {
3
25
  if ($("#chartAnchor").length == 0) {
4
26
  return;
5
27
  }
6
28
 
7
- d3.json($("#chartAnchor").data("reports-path"), function (error, json) {
29
+ d3.json($("#chartAnchor").data("reports-path")).then(function (json) {
8
30
  $("#loading-spinner").remove();
9
31
 
10
32
  var renderer = new Kiddo.Renderer("#chartAnchor");
11
33
 
12
- if (error) {
13
- ajaxErrorAlert(error);
14
- return renderer.noData();
15
- }
16
-
17
34
  var data = json[0];
18
35
 
19
36
  if (
@@ -49,6 +66,12 @@
49
66
  console.log(ex);
50
67
  renderer.noData();
51
68
  }
69
+ }).catch(function (error) {
70
+ $("#loading-spinner").remove();
71
+
72
+ var renderer = new Kiddo.Renderer("#chartAnchor");
73
+ renderError(errorMessage(error));
74
+ return renderer.noData();
52
75
  });
53
76
  });
54
77
  })(d3, jQuery, window, document);
@@ -329,11 +329,25 @@
329
329
 
330
330
  /* app/views/kanaui/reports/index.html.erb */
331
331
 
332
+ .kanaui-flash-container {
333
+ margin: 1rem 0 1.5rem;
334
+ position: relative;
335
+ z-index: 1;
336
+ }
337
+
338
+ .kanaui-flash-container .alert {
339
+ margin-bottom: 0.75rem;
340
+ }
341
+
332
342
  .kanaui-reports-index .configured-reports {
333
343
  max-width: 80rem;
334
344
  width: 100%;
335
345
  }
336
346
 
347
+ .kanaui-reports-index .kanaui-report-notice {
348
+ margin-bottom: 1rem;
349
+ }
350
+
337
351
  .kanaui-reports-index .configured-reports .configured-reports-header {
338
352
  display: flex;
339
353
  align-items: start;
@@ -393,6 +407,11 @@
393
407
  color: #d92d20 !important;
394
408
  }
395
409
 
410
+ .kanaui-reports-index .disabled-table-button {
411
+ color: #98a2b3 !important;
412
+ cursor: not-allowed;
413
+ }
414
+
396
415
  .kanaui-reports-index .edit-button {
397
416
  color: #535862 !important;
398
417
  }
@@ -3,6 +3,91 @@
3
3
  white-space: nowrap;
4
4
  }
5
5
 
6
+ .kanaui-field-control {
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 8px;
10
+ }
11
+
12
+ .kanaui-field-control-top {
13
+ align-items: flex-start;
14
+ }
15
+
16
+ .kanaui-field-control .form-control {
17
+ flex: 1 1 auto;
18
+ }
19
+
20
+ .kanaui-field-help {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ flex: 0 0 auto;
25
+ width: 22px;
26
+ height: 22px;
27
+ padding: 0;
28
+ color: #6b7280;
29
+ background: transparent;
30
+ border: 0;
31
+ box-shadow: none;
32
+ font-size: 16px;
33
+ font-weight: 600;
34
+ line-height: 1;
35
+ text-align: center;
36
+ cursor: pointer;
37
+ }
38
+
39
+ .kanaui-field-help:hover,
40
+ .kanaui-field-help:focus {
41
+ color: #111827;
42
+ background: transparent;
43
+ outline: none;
44
+ }
45
+
46
+ .kanaui-field-help:focus {
47
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.24);
48
+ border-radius: 3px;
49
+ }
50
+
51
+ .kanaui-field-tooltip {
52
+ position: absolute;
53
+ z-index: 1070;
54
+ display: none;
55
+ max-width: 360px;
56
+ padding: 10px 12px;
57
+ color: #1f2937;
58
+ background-color: #fff;
59
+ border: 1px solid #d1d5db;
60
+ border-radius: 6px;
61
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
62
+ font-size: 14px;
63
+ font-weight: 400;
64
+ line-height: 1.5;
65
+ pointer-events: none;
66
+ }
67
+
68
+ .kanaui-field-tooltip::before {
69
+ content: "";
70
+ position: absolute;
71
+ top: 12px;
72
+ left: -6px;
73
+ width: 0;
74
+ height: 0;
75
+ border-top: 6px solid transparent;
76
+ border-bottom: 6px solid transparent;
77
+ border-right: 6px solid #fff;
78
+ }
79
+
80
+ .kanaui-field-tooltip.is-flipped::before {
81
+ left: auto;
82
+ right: -6px;
83
+ border-right: 0;
84
+ border-left: 6px solid #fff;
85
+ }
86
+
87
+ .kanaui-field-tooltip.is-visible {
88
+ display: block;
89
+ }
90
+
6
91
  .sql-query {
7
92
  word-break: break-word;
8
93
  }
@@ -11,7 +11,7 @@ module Kanaui
11
11
 
12
12
  @raw_name = (params[:name] || '').split('^')[0]
13
13
 
14
- @end_date = params[:end_date] || Date.today.to_s
14
+ @end_date = params[:end_date] || Time.zone.today.to_s
15
15
 
16
16
  @available_start_dates = start_date_options
17
17
  @start_date = params[:start_date] || (params[:delta_days].present? ? (@end_date.to_date - params[:delta_days].to_i.day).to_s : @available_start_dates['Last 6 months'])
@@ -19,9 +19,13 @@ module Kanaui
19
19
  @reports = JSON.parse(raw_reports)
20
20
  @report = current_report(@reports) || {}
21
21
 
22
- # If no report name is provided, redirect to the default (second) report
23
- if @raw_name.blank? && @reports.is_a?(Array) && @reports[1].present?
24
- default_name = @reports[1]['reportName']
22
+ # If no report name is provided, redirect to a default report.
23
+ # Prefer the historical default (second report) when present, but fall back
24
+ # to the first report so a single configured report still renders the
25
+ # dashboard controls and chart area.
26
+ default_report = @reports[1] || @reports[0] if @reports.is_a?(Array)
27
+ default_name = default_report&.fetch('reportName', nil).presence
28
+ if @raw_name.blank? && default_name.present?
25
29
  query_params = { start_date: @start_date,
26
30
  end_date: @end_date,
27
31
  name: default_name,
@@ -29,8 +33,8 @@ module Kanaui
29
33
  sql_only: params[:sql_only],
30
34
  format: params[:format] }
31
35
 
32
- query_params[:fake] = params[:fake] unless params[:fake].blank?
33
- query_params[:type] = params[:type] unless params[:type].blank?
36
+ query_params[:fake] = params[:fake] if params[:fake].present?
37
+ query_params[:type] = params[:type] if params[:type].present?
34
38
 
35
39
  redirect_to dashboard_index_path(query_params) and return
36
40
  end
@@ -46,14 +50,14 @@ module Kanaui
46
50
  name = query.present? ? "#{params[:name]}#{query}^metric:count" : params[:name]
47
51
  query_params = { start_date: @start_date,
48
52
  end_date: @end_date,
49
- name: name,
53
+ name:,
50
54
  smooth: params[:smooth],
51
55
  sql_only: params[:sql_only],
52
56
  format: params[:format] }
53
57
 
54
58
  # Test only
55
- query_params[:fake] = params[:fake] unless params[:fake].blank?
56
- query_params[:type] = params[:type] unless params[:type].blank?
59
+ query_params[:fake] = params[:fake] if params[:fake].present?
60
+ query_params[:type] = params[:type] if params[:type].present?
57
61
 
58
62
  redirect_to dashboard_index_path(query_params) and return
59
63
  end
@@ -86,12 +90,12 @@ module Kanaui
86
90
  respond_to do |fmt|
87
91
  fmt.csv do
88
92
  filename = params[:name]
89
- unless params[:start_date].blank?
93
+ if params[:start_date].present?
90
94
  filename += "_#{params[:start_date]}"
91
- filename += "-#{params[:end_date]}" unless params[:end_date].blank?
95
+ filename += "-#{params[:end_date]}" if params[:end_date].present?
92
96
  end
93
97
  filename += '.csv'
94
- send_data(raw_reports, filename: filename)
98
+ send_data(raw_reports, filename:)
95
99
  end
96
100
  fmt.all { render json: reports }
97
101
  end
@@ -146,10 +150,10 @@ module Kanaui
146
150
  filters.each do |k, v|
147
151
  next if v.blank?
148
152
 
149
- filter_query << '%26' unless filter_query.blank?
153
+ filter_query.presence&.<<('%26')
150
154
  filter_query << "(#{k}=#{v.join("|#{k}=")})"
151
155
  end
152
- query << "^filter:#{filter_query}" unless filter_query.blank?
156
+ query << "^filter:#{filter_query}" if filter_query.present?
153
157
 
154
158
  groups.each do |k, v|
155
159
  next if v.blank?
@@ -27,8 +27,27 @@ module Kanaui
27
27
  end
28
28
 
29
29
  rescue_from(KillBillClient::API::ResponseError) do |killbill_exception|
30
- flash[:error] = "Error while communicating with the Kill Bill server: #{as_string(killbill_exception)}"
31
- redirect_to dashboard_index_path
30
+ error_message = I18n.t('kanaui.errors.killbill_communication', error: as_string(killbill_exception))
31
+
32
+ if json_request?
33
+ render json: { message: error_message }, status: killbill_exception.response.code.to_i
34
+ else
35
+ flash[:error] = error_message
36
+ # Avoid redirect loops when the dashboard itself cannot be loaded.
37
+ if controller_name == 'dashboard' && action_name == 'index'
38
+ render plain: error_message, status: killbill_exception.response.code.to_i
39
+ else
40
+ redirect_to dashboard_index_path
41
+ end
42
+ end
43
+ end
44
+
45
+ def json_request?
46
+ request.format.json? || params[:format] == 'json' || dashboard_reports_json_request?
47
+ end
48
+
49
+ def dashboard_reports_json_request?
50
+ controller_name == 'dashboard' && action_name == 'reports' && params[:format].blank?
32
51
  end
33
52
 
34
53
  def as_string(e)
@@ -4,48 +4,57 @@ module Kanaui
4
4
  class ReportsController < Kanaui::EngineController
5
5
  def index
6
6
  @reports = JSON.parse(Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient)).map(&:deep_symbolize_keys)
7
+ @report_notice = report_notice_from_flash
7
8
  end
8
9
 
9
10
  def new
10
11
  @report = {}
11
12
  end
12
13
 
13
- def create
14
- Kanaui::DashboardHelper::DashboardApi.create_report(report_from_params.to_json, options_for_klient)
15
-
16
- flash[:notice] = 'Report successfully created'
17
- redirect_to action: :index
18
- end
19
-
20
14
  def edit
21
15
  @report = JSON.parse(Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient))
22
16
  .find { |x| x['reportName'] == params.require(:id) }
23
17
  .deep_symbolize_keys
24
18
  end
25
19
 
20
+ def create
21
+ Kanaui::DashboardHelper::DashboardApi.create_report(report_from_params.to_json, options_for_klient)
22
+
23
+ redirect_to_index_with_notice(:created)
24
+ end
25
+
26
26
  def update
27
27
  Kanaui::DashboardHelper::DashboardApi.update_report(params.require(:id), report_from_params.to_json, options_for_klient)
28
28
 
29
- flash[:notice] = 'Report successfully updated'
30
- redirect_to action: :index
29
+ redirect_to_index_with_notice(:updated)
31
30
  end
32
31
 
33
32
  def refresh
34
33
  Kanaui::DashboardHelper::DashboardApi.refresh_report(params.require(:id), options_for_klient)
35
34
 
36
- flash[:notice] = 'Report refresh successfully scheduled'
37
- redirect_to action: :index
35
+ redirect_to_index_with_notice(:refresh_scheduled)
38
36
  end
39
37
 
40
38
  def destroy
41
39
  Kanaui::DashboardHelper::DashboardApi.delete_report(params.require(:id), options_for_klient)
42
40
 
43
- flash[:notice] = 'Report successfully deleted'
44
- redirect_to action: :index
41
+ redirect_to_index_with_notice(:deleted)
45
42
  end
46
43
 
47
44
  private
48
45
 
46
+ def report_notice_from_flash
47
+ notice_key = flash[:report_notice].presence&.to_s
48
+ return nil unless %w[created updated refresh_scheduled deleted].include?(notice_key)
49
+
50
+ I18n.t("kanaui.reports.notices.#{notice_key}", default: nil)
51
+ end
52
+
53
+ def redirect_to_index_with_notice(notice_key)
54
+ flash[:report_notice] = notice_key
55
+ redirect_to action: :index
56
+ end
57
+
49
58
  def report_from_params
50
59
  {
51
60
  reportName: params[:report_name],