kanaui 5.0.0 → 5.0.2

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: 0c74b8f72b208b13b20e079654c00f793b3d0c663cd33f9dbb4957aa6fd420e6
4
+ data.tar.gz: 859e32a01514363bb64f4c3772c0d6b70b2ed72fb0daa0d4bda8facef7bba877
5
5
  SHA512:
6
- metadata.gz: 4d5f2cf67c9412dde6dbf0e74abb92557bd47b90180a5f9c6a41ec5a8d2a1911ed321e4429cc695a7ea4aa72c26b2c78db27d102b1bed1d54023658ecf46d22b
7
- data.tar.gz: 965d83c8c2ea050106c85e5568a282573c5b70cd1679e78c80683ed6a96ca1206bcda2dbe10cecd1df7d774387bc56a4071d01b7f5368a7fa06249914642585b
6
+ metadata.gz: 52219b23e1cdd624809a26bc71ab8ea903d3c649ff5e5e6d3fe74538c850341680a189d9e50f1c19097df907430b49dc27009f63319a27f110635b3a4afa6589
7
+ data.tar.gz: 2606b237d12c8e5f4ecf5bcca19bed63ef34eb38c0adb862f4924f3e3d6afe898fdccf53474d328d49cbaa9026d38253f88e4306f5049937c9fd46ccb4601336
@@ -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,80 +13,79 @@
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
- // Create legend container at the top
71
+ // Create legend container to the right of the chart area
71
72
  var legendContainer = svg
72
73
  .append("g")
73
74
  .attr("class", "chart-legend")
74
- .attr("transform", "translate(" + (self.width - 100) + ", -25)");
75
+ .attr("transform", "translate(" + (self.width + self.margin_left + 15) + ", 10)");
75
76
 
76
77
  // Calculate total values for legend
77
78
  var legendData = datasets.map(function (dataset, index) {
78
79
  var latestValue = dataset.values[dataset.values.length - 1];
79
- var totalCount = dataset.values.length;
80
80
  return {
81
81
  name: dataset.name,
82
82
  value: latestValue ? latestValue.y : 0,
83
- count: totalCount,
84
83
  color: self.color(dataset.name),
85
84
  index: index,
86
85
  };
87
86
  });
88
87
 
89
- // Create legend items
88
+ // Create legend items as a vertical column
90
89
  var legendItems = legendContainer
91
90
  .selectAll(".legend-item")
92
91
  .data(legendData)
@@ -94,34 +93,30 @@
94
93
  .append("g")
95
94
  .attr("class", "legend-item");
96
95
 
97
- var xOffset = 0;
98
- legendItems.each(function (d, i) {
96
+ var yOffset = 0;
97
+ legendItems.each(function (d) {
99
98
  var legendItem = d3.select(this);
100
99
 
101
- // Add colored circle
102
100
  legendItem
103
101
  .append("circle")
104
- .attr("cx", xOffset + 6)
105
- .attr("cy", 0)
102
+ .attr("cx", 6)
103
+ .attr("cy", yOffset)
106
104
  .attr("r", 6)
107
105
  .style("fill", d.color);
108
106
 
109
- // Add text label
110
107
  var labelText =
111
- d.name + " :: " + d.count + ": " + d3.format(",.2f")(d.value);
108
+ helper.formatSeriesName(d.name, self.reportName) + ": " + helper.formatValue(d.value);
112
109
  legendItem
113
110
  .append("text")
114
- .attr("x", xOffset + 18)
115
- .attr("y", 0)
111
+ .attr("x", 18)
112
+ .attr("y", yOffset)
116
113
  .attr("dy", "0.35em")
117
114
  .style("font-size", "0.875rem")
118
115
  .style("font-weight", "500")
119
116
  .style("fill", "#6B7280")
120
117
  .text(labelText);
121
118
 
122
- // Calculate width for next item
123
- var textWidth = this.getBBox().width;
124
- xOffset += textWidth + 40; // Add spacing between items
119
+ yOffset += 22;
125
120
  });
126
121
 
127
122
  // Render data lines
@@ -129,12 +124,6 @@
129
124
  var data = dataset.values,
130
125
  name = dataset.name;
131
126
 
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
127
  svg
139
128
  .append("path")
140
129
  .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");
@@ -59,15 +59,16 @@
59
59
  var infoBox = info.node().getBBox();
60
60
  var infoTitleBox = infoTitle.node().getBBox();
61
61
  var margin = 40;
62
+ var minWidth = Math.max(box.width, infoTitleBox.width) + margin;
62
63
 
63
64
  info.attr("height", infoBox.height + box.height + 7);
64
- if (infoBox.width < box.width) {
65
- info.attr("width", box.width + margin);
66
- infoTitleBg.attr("width", box.width + margin);
65
+ if (infoBox.width < minWidth) {
66
+ info.attr("width", minWidth);
67
+ infoTitleBg.attr("width", minWidth);
67
68
 
68
69
  $("#mouseover_canvas #info-title").attr(
69
70
  "dx",
70
- (box.width + margin) / 2 - infoTitleBox.width / 2
71
+ minWidth / 2 - infoTitleBox.width / 2
71
72
  );
72
73
  }
73
74
  };
@@ -80,8 +81,10 @@
80
81
  .attr("transform", "translate(" + self.margin_left + ",0)");
81
82
  });
82
83
 
83
- function mousemove() {
84
- var _this = this;
84
+ function mousemove(event) {
85
+ var pointer = d3.pointer(event, this);
86
+ var mouseX = pointer[0];
87
+ var mouseY = pointer[1];
85
88
 
86
89
  $("#mouseover_canvas .chart_values").detach().remove();
87
90
  $("#mouseover_canvas .chart_circles").detach().remove();
@@ -94,7 +97,7 @@
94
97
  var data = element.values;
95
98
  var name = element.name;
96
99
 
97
- var x0 = x.invert(d3.mouse(_this)[0]),
100
+ var x0 = x.invert(mouseX),
98
101
  i = helper.bisectDate(data, x0, 1),
99
102
  d0 = data[i - 1],
100
103
  d1 = data[i];
@@ -115,11 +118,7 @@
115
118
  .attr("cy", y(d.y))
116
119
  .style("fill", self.color(name));
117
120
 
118
- var canvasPosition = x(x0) > self.width / 2 ? 50 : self.width / 2;
119
-
120
- canvas.attr("transform", "translate(" + canvasPosition + ",0)");
121
-
122
- canvas.select("#info-title").text(d.date);
121
+ canvas.select("#info-title").text(helper.formatDate(d.date));
123
122
 
124
123
  elementsForLegend.push({ element: element, d: d });
125
124
  });
@@ -150,11 +149,22 @@
150
149
  .text(
151
150
  element.d === undefined
152
151
  ? element.element.name
153
- : helper.formatValueDisplay(element.element.name, element.d)
152
+ : helper.formatValueDisplay(element.element.name, element.d, self.reportName)
154
153
  );
155
154
 
156
155
  addInfoDimensions(text);
157
156
  });
157
+
158
+ // Position tooltip to follow the cursor; flip left when near the right edge.
159
+ var tooltipOffset = 15;
160
+ var tooltipWidth = info.node().getBBox().width;
161
+ var tooltipHeight = info.node().getBBox().height;
162
+ var localX = (mouseX + tooltipOffset + tooltipWidth > self.width)
163
+ ? mouseX - tooltipWidth - tooltipOffset
164
+ : mouseX + tooltipOffset;
165
+ var canvasX = localX + self.margin_left;
166
+ var canvasY = Math.max(0, mouseY - 150);
167
+ canvas.attr("transform", "translate(" + canvasX + "," + canvasY + ")");
158
168
  }
159
169
  },
160
170
  };
@@ -1,19 +1,106 @@
1
1
  (function (Kiddo, d3) {
2
2
  Kiddo.Helper = function () {
3
+ var formatValue = function (d) {
4
+ return d % 1 === 0 ? d3.format(",d")(d) : d3.format(",.2f")(d);
5
+ };
3
6
  var formatCurrency = function (d) {
4
7
  return "$" + formatValue(d);
5
8
  };
6
- var formatValue = d3.format(",.2f");
9
+
10
+ var humanizeSegment = function (segment) {
11
+ segment = String(segment || "")
12
+ .replace(/[_-]+/g, " ")
13
+ .replace(/\s+/g, " ")
14
+ .trim();
15
+
16
+ if (!segment) {
17
+ return "";
18
+ }
19
+
20
+ return segment
21
+ .split(" ")
22
+ .map(function (word) {
23
+ if (/^\d+(\.\d+)?$/.test(word)) {
24
+ return word;
25
+ }
26
+ if (/^[A-Z]{2,3}$/.test(word)) {
27
+ return word;
28
+ }
29
+
30
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
31
+ })
32
+ .join(" ");
33
+ };
34
+
35
+ var formatSeriesName = function (name, reportName) {
36
+ return String(name || "")
37
+ .split(/\s*::\s*/)
38
+ .filter(function (segment) {
39
+ // Remove numeric-only segments (tenant record id)
40
+ return !/^\d+$/.test(segment.trim());
41
+ })
42
+ .map(function (segment) {
43
+ var parts = segment.split(/\s*:\s*/);
44
+ if (parts.length > 1) {
45
+ var qualifier = humanizeSegment(parts.slice(1).join(": "));
46
+ var label = (reportName && parts[0].trim().toLowerCase() === "count")
47
+ ? reportName
48
+ : humanizeSegment(parts[0]);
49
+
50
+ // For count-based reports, a numeric qualifier is usually tenant id
51
+ // and should not be displayed in UI labels.
52
+ if (reportName && parts[0].trim().toLowerCase() === "count" && /^\d+$/.test(qualifier)) {
53
+ return label;
54
+ }
55
+
56
+ return label + " (" + qualifier + ")";
57
+ }
58
+ return humanizeSegment(segment);
59
+ })
60
+ .filter(function (segment) {
61
+ return segment.length > 0;
62
+ })
63
+ .join(" : ");
64
+ };
65
+
66
+ // Extracts just the qualifier part (e.g. "EUR" from "count: EUR :: 1") for compact tooltip labels
67
+ var formatSeriesLabel = function (name) {
68
+ var segments = String(name || "")
69
+ .split(/\s*::\s*/)
70
+ .filter(function (segment) {
71
+ return !/^\d+$/.test(segment.trim());
72
+ });
73
+
74
+ if (segments.length === 0) return humanizeSegment(name);
75
+
76
+ var parts = segments[0].split(/\s*:\s*/);
77
+ if (parts.length > 1) {
78
+ return humanizeSegment(parts.slice(1).join(": "));
79
+ }
80
+ return humanizeSegment(segments[0]);
81
+ };
82
+
83
+ var parseDateFn = d3.timeParse("%Y-%m-%d");
84
+ var formatDateFn = d3.timeFormat("%b %d, %Y");
85
+ var formatDate = function (dateStr) {
86
+ var parsed = parseDateFn(dateStr);
87
+ return parsed ? formatDateFn(parsed) : dateStr;
88
+ };
7
89
 
8
90
  return {
9
- parseDate: d3.time.format("%Y-%m-%d").parse,
91
+ parseDate: parseDateFn,
10
92
  bisectDate: d3.bisector(function (d) {
11
93
  return d.x;
12
94
  }).left,
13
95
  formatCurrency: formatCurrency,
14
96
  formatValue: formatValue,
15
- formatValueDisplay: function (name, d) {
16
- return name + ": " + formatValue(d.y); // Add currency boolean on backend later -- formatCurrency(d.y); }
97
+ formatSeriesName: formatSeriesName,
98
+ formatDate: formatDate,
99
+ formatValueDisplay: function (name, d, reportName) {
100
+ var seriesLabel = reportName
101
+ ? formatSeriesName(name, reportName)
102
+ : formatSeriesLabel(name);
103
+ return seriesLabel + ": " + formatValue(d.y);
17
104
  },
18
105
  };
19
106
  };
@@ -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);
@@ -19,6 +19,7 @@
19
19
  raw_height: raw_height,
20
20
  width: raw_width - margin_left - margin_right,
21
21
  height: raw_height - margin_top - margin_bottom,
22
+ reportName: $("#chartAnchor").data("report-name") || "",
22
23
  };
23
24
  };
24
25
  })((window.Kiddo = window.Kiddo || {}), d3);