refactor: replace flot with highcharts (#4030)
* chore: Replace flot with highcharts Since flot hasn't been updated since 2014 and was only used in one place. Simplify how highcharts is initialized and used, and re-enable pre-bs5 export functionality. * Fix tests * Remove some console.log statements
This commit is contained in:
parent
337d2ad387
commit
74f2a85d64
11
.pnp.cjs
generated
11
.pnp.cjs
generated
|
@ -45,7 +45,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
["cypress-real-events", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:1.7.0"],\
|
["cypress-real-events", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:1.7.0"],\
|
||||||
["d3", "npm:7.4.4"],\
|
["d3", "npm:7.4.4"],\
|
||||||
["eslint", "npm:8.16.0"],\
|
["eslint", "npm:8.16.0"],\
|
||||||
["flot", "npm:4.2.2"],\
|
|
||||||
["highcharts", "npm:10.1.0"],\
|
["highcharts", "npm:10.1.0"],\
|
||||||
["html-validate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:7.1.1"],\
|
["html-validate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:7.1.1"],\
|
||||||
["jquery", "npm:3.6.0"],\
|
["jquery", "npm:3.6.0"],\
|
||||||
|
@ -3283,15 +3282,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["flot", [\
|
|
||||||
["npm:4.2.2", {\
|
|
||||||
"packageLocation": "./.yarn/cache/flot-npm-4.2.2-77eb146093-81910f7bc2.zip/node_modules/flot/",\
|
|
||||||
"packageDependencies": [\
|
|
||||||
["flot", "npm:4.2.2"]\
|
|
||||||
],\
|
|
||||||
"linkType": "HARD"\
|
|
||||||
}]\
|
|
||||||
]],\
|
|
||||||
["forever-agent", [\
|
["forever-agent", [\
|
||||||
["npm:0.6.1", {\
|
["npm:0.6.1", {\
|
||||||
"packageLocation": "./.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-766ae6e220.zip/node_modules/forever-agent/",\
|
"packageLocation": "./.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-766ae6e220.zip/node_modules/forever-agent/",\
|
||||||
|
@ -5188,7 +5178,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
["cypress-real-events", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:1.7.0"],\
|
["cypress-real-events", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:1.7.0"],\
|
||||||
["d3", "npm:7.4.4"],\
|
["d3", "npm:7.4.4"],\
|
||||||
["eslint", "npm:8.16.0"],\
|
["eslint", "npm:8.16.0"],\
|
||||||
["flot", "npm:4.2.2"],\
|
|
||||||
["highcharts", "npm:10.1.0"],\
|
["highcharts", "npm:10.1.0"],\
|
||||||
["html-validate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:7.1.1"],\
|
["html-validate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:7.1.1"],\
|
||||||
["jquery", "npm:3.6.0"],\
|
["jquery", "npm:3.6.0"],\
|
||||||
|
|
BIN
.yarn/cache/flot-npm-4.2.2-77eb146093-81910f7bc2.zip
vendored
BIN
.yarn/cache/flot-npm-4.2.2-77eb146093-81910f7bc2.zip
vendored
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
import "flot";
|
|
|
@ -1 +0,0 @@
|
||||||
import "highcharts/modules/export-data";
|
|
|
@ -1 +0,0 @@
|
||||||
import "highcharts/modules/exporting";
|
|
|
@ -1,3 +0,0 @@
|
||||||
import * as Highcharts from "highcharts";
|
|
||||||
|
|
||||||
window.Highcharts = Highcharts;
|
|
|
@ -1,3 +0,0 @@
|
||||||
import * as Highcharts from "highcharts/highstock";
|
|
||||||
|
|
||||||
window.Highcharts = Highcharts;
|
|
|
@ -1 +0,0 @@
|
||||||
import "highcharts/modules/offline-exporting";
|
|
13
ietf/static/js/highcharts.js
Normal file
13
ietf/static/js/highcharts.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Highcharts from "highcharts";
|
||||||
|
|
||||||
|
import Highcharts_Exporting from "highcharts/modules/exporting";
|
||||||
|
import Highcharts_Offline_Exporting from "highcharts/modules/offline-exporting";
|
||||||
|
import Highcharts_Export_Data from "highcharts/modules/export-data";
|
||||||
|
import Highcharts_Accessibility from"highcharts/modules/accessibility";
|
||||||
|
|
||||||
|
Highcharts_Exporting(Highcharts);
|
||||||
|
Highcharts_Offline_Exporting(Highcharts);
|
||||||
|
Highcharts_Export_Data(Highcharts);
|
||||||
|
Highcharts_Accessibility(Highcharts);
|
||||||
|
|
||||||
|
window.Highcharts = Highcharts;
|
13
ietf/static/js/highstock.js
Normal file
13
ietf/static/js/highstock.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Highcharts from "highcharts/highstock";
|
||||||
|
|
||||||
|
import Highcharts_Exporting from "highcharts/modules/exporting";
|
||||||
|
import Highcharts_Offline_Exporting from "highcharts/modules/offline-exporting";
|
||||||
|
import Highcharts_Export_Data from "highcharts/modules/export-data";
|
||||||
|
import Highcharts_Accessibility from"highcharts/modules/accessibility";
|
||||||
|
|
||||||
|
Highcharts_Exporting(Highcharts);
|
||||||
|
Highcharts_Offline_Exporting(Highcharts);
|
||||||
|
Highcharts_Export_Data(Highcharts);
|
||||||
|
Highcharts_Accessibility(Highcharts);
|
||||||
|
|
||||||
|
window.Highcharts = Highcharts;
|
|
@ -1,9 +0,0 @@
|
||||||
$(document)
|
|
||||||
.ready(function () {
|
|
||||||
if (window.timeSeriesData && window.timeSeriesOptions) {
|
|
||||||
var placeholder = $(".stats-time-graph");
|
|
||||||
placeholder.height(Math.round(placeholder.width() * 1 / 3));
|
|
||||||
|
|
||||||
$.plot(placeholder, window.timeSeriesData, window.timeSeriesOptions);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -18,7 +18,6 @@ $(document)
|
||||||
var row = form.find(selector);
|
var row = form.find(selector);
|
||||||
if (!row.is(".row"))
|
if (!row.is(".row"))
|
||||||
row = row.closest(".row");
|
row = row.closest(".row");
|
||||||
console.log("!");
|
|
||||||
if ($.inArray(selector, shouldBeVisible[val]) != -1)
|
if ($.inArray(selector, shouldBeVisible[val]) != -1)
|
||||||
row.show();
|
row.show();
|
||||||
else
|
else
|
||||||
|
@ -27,4 +26,4 @@ $(document)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.trigger("change");
|
.trigger("change");
|
||||||
});
|
});
|
||||||
|
|
|
@ -209,7 +209,7 @@ class StatisticsTests(TestCase):
|
||||||
{"label": "late", "color": "#b42222", "data": [[expected_js_timestamp, 0]]}
|
{"label": "late", "color": "#b42222", "data": [[expected_js_timestamp, 0]]}
|
||||||
])
|
])
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
self.assertTrue(q('.stats-time-graph'))
|
self.assertTrue(q('#stats-time-graph'))
|
||||||
|
|
||||||
# check non-stacked chart
|
# check non-stacked chart
|
||||||
url = urlreverse(ietf.stats.views.review_stats, kwargs={ "stats_type": "time" })
|
url = urlreverse(ietf.stats.views.review_stats, kwargs={ "stats_type": "time" })
|
||||||
|
@ -219,7 +219,7 @@ class StatisticsTests(TestCase):
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertEqual(json.loads(r.context['data']), [{"color": "#3d22b3", "data": [[expected_js_timestamp, 0]]}])
|
self.assertEqual(json.loads(r.context['data']), [{"color": "#3d22b3", "data": [[expected_js_timestamp, 0]]}])
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
self.assertTrue(q('.stats-time-graph'))
|
self.assertTrue(q('#stats-time-graph'))
|
||||||
|
|
||||||
# check reviewer level
|
# check reviewer level
|
||||||
url = urlreverse(ietf.stats.views.review_stats, kwargs={ "stats_type": "completion", "acronym": review_req.team.acronym })
|
url = urlreverse(ietf.stats.views.review_stats, kwargs={ "stats_type": "completion", "acronym": review_req.team.acronym })
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
function stateChanged() {
|
function stateChanged() {
|
||||||
var v = $(this).val();
|
var v = $(this).val();
|
||||||
console.log(v);
|
|
||||||
$("#id_message").val(info_msg[v] || "");
|
$("#id_message").val(info_msg[v] || "");
|
||||||
|
|
||||||
if ($.inArray(+v, statesForBallotWoExtern) != -1)
|
if ($.inArray(+v, statesForBallotWoExtern) != -1)
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load ietf_filters %}
|
{% load ietf_filters %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="{% static 'ietf/js/highcharts-highstock.js' %}"></script>
|
<script src="{% static 'ietf/js/highstock.js' %}"></script>
|
||||||
<script src="{% static 'ietf/js/highcharts-exporting.js' %}"></script>
|
|
||||||
<script src="{% static 'ietf/js/highcharts-offline-exporting.js' %}"></script>
|
|
||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
var chart;
|
var chart;
|
||||||
|
@ -25,4 +23,4 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% origin %}
|
{% origin %}
|
||||||
<div id="chart"></div>
|
<div id="chart"></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -165,11 +165,11 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="{% static "ietf/js/list.js" %}"></script>
|
<script src="{% static "ietf/js/list.js" %}"></script>
|
||||||
<script src="{% static 'ietf/js/highcharts-highstock.js' %}"></script>
|
<script src="{% static 'ietf/js/highstock.js' %}"></script>
|
||||||
<script src="{% static 'ietf/js/highcharts-exporting.js' %}"></script>
|
|
||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
{% for person in persons %}
|
{% for person in persons %}
|
||||||
|
{% if person.has_drafts %}
|
||||||
$.getJSON('{% url "ietf.doc.views_stats.chart_conf_person_drafts" id=person.id %}', function (conf) {
|
$.getJSON('{% url "ietf.doc.views_stats.chart_conf_person_drafts" id=person.id %}', function (conf) {
|
||||||
// Create the chart
|
// Create the chart
|
||||||
chart = Highcharts.stockChart('chart-{{ forloop.counter }}', conf);
|
chart = Highcharts.stockChart('chart-{{ forloop.counter }}', conf);
|
||||||
|
@ -179,7 +179,8 @@
|
||||||
chart.hideLoading();
|
chart.hideLoading();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
});
|
{% endfor %}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -79,9 +79,7 @@
|
||||||
{% include content_template %}
|
{% include content_template %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="{% static 'ietf/js/highcharts-highcharts.js' %}"></script>
|
<script src="{% static 'ietf/js/highcharts.js' %}"></script>
|
||||||
<script src="{% static 'ietf/js/highcharts-exporting.js' %}"></script>
|
|
||||||
<script src="{% static 'ietf/js/highcharts-offline-exporting.js' %}"></script>
|
|
||||||
<script src="{% static 'ietf/js/stats.js' %}"></script>
|
<script src="{% static 'ietf/js/stats.js' %}"></script>
|
||||||
<script src="{% static "ietf/js/list.js" %}"></script>
|
<script src="{% static "ietf/js/list.js" %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -27,7 +27,6 @@
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
formatter: function () {
|
formatter: function () {
|
||||||
console.log(this);
|
|
||||||
var s = '<b>' + this.points[0].key + '</b>';
|
var s = '<b>' + this.points[0].key + '</b>';
|
||||||
|
|
||||||
$.each(this.points, function () {
|
$.each(this.points, function () {
|
||||||
|
@ -61,4 +60,4 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
|
@ -27,7 +27,6 @@
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
formatter: function () {
|
formatter: function () {
|
||||||
console.log(this);
|
|
||||||
var s = '<b>' + this.points[0].key + '</b>';
|
var s = '<b>' + this.points[0].key + '</b>';
|
||||||
|
|
||||||
$.each(this.points, function () {
|
$.each(this.points, function () {
|
||||||
|
@ -61,4 +60,4 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
|
@ -28,10 +28,7 @@
|
||||||
<div class="document-stats">{% include content_template %}</div>
|
<div class="document-stats">{% include content_template %}</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="{% static 'ietf/js/highcharts-highcharts.js' %}"></script>
|
<script src="{% static 'ietf/js/highcharts.js' %}"></script>
|
||||||
<script src="{% static 'ietf/js/highcharts-exporting.js' %}"></script>
|
|
||||||
<script src="{% static 'ietf/js/highcharts-export-data.js' %}"></script>
|
|
||||||
<script src="{% static 'ietf/js/highcharts-offline-exporting.js' %}"></script>
|
|
||||||
<script src="{% static 'ietf/js/stats.js' %}"></script>
|
<script src="{% static 'ietf/js/stats.js' %}"></script>
|
||||||
<script src="{% static "ietf/js/list.js" %}"></script>
|
<script src="{% static "ietf/js/list.js" %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -274,11 +274,10 @@
|
||||||
<h2>
|
<h2>
|
||||||
Counts per month
|
Counts per month
|
||||||
</h2>
|
</h2>
|
||||||
<div class="stats-time-graph">
|
<div id="stats-time-graph">
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var timeSeriesData = {{ data|safe }};
|
var timeSeriesData = {{ data|safe }};
|
||||||
console.log(timeSeriesData);
|
|
||||||
var timeSeriesOptions = {
|
var timeSeriesOptions = {
|
||||||
xaxis: {
|
xaxis: {
|
||||||
mode: "time",
|
mode: "time",
|
||||||
|
@ -333,9 +332,39 @@ href="{{ t.reviewer_stats_url }}">
|
||||||
<script src="{% static 'ietf/js/datepicker.js' %}">
|
<script src="{% static 'ietf/js/datepicker.js' %}">
|
||||||
</script>
|
</script>
|
||||||
{% if stats_type == "time" %}
|
{% if stats_type == "time" %}
|
||||||
<script src="{% static 'ietf/js/flot.js' %}">
|
<script src="{% static 'ietf/js/highcharts.js' %}"></script>
|
||||||
</script>
|
<script>
|
||||||
<script src="{% static 'ietf/js/review-stats.js' %}">
|
$(document)
|
||||||
</script>
|
.ready(function () {
|
||||||
|
if (window.timeSeriesData) {
|
||||||
|
series = window.timeSeriesData.map(({label, data, color}) => ({
|
||||||
|
name: label,
|
||||||
|
data: data,
|
||||||
|
color: color,
|
||||||
|
type: "column"
|
||||||
|
}));
|
||||||
|
Highcharts.chart("stats-time-graph", {
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: undefined,
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: undefined,
|
||||||
|
},
|
||||||
|
plotOptions: {
|
||||||
|
column: {
|
||||||
|
stacking: 'normal',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: series
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
10
package.json
10
package.json
|
@ -12,7 +12,6 @@
|
||||||
"bootstrap-icons": "1.8.2",
|
"bootstrap-icons": "1.8.2",
|
||||||
"caniuse-lite": "1.0.30001342",
|
"caniuse-lite": "1.0.30001342",
|
||||||
"d3": "7.4.4",
|
"d3": "7.4.4",
|
||||||
"flot": "4.2.2",
|
|
||||||
"highcharts": "10.1.0",
|
"highcharts": "10.1.0",
|
||||||
"jquery": "3.6.0",
|
"jquery": "3.6.0",
|
||||||
"jquery-ui-dist": "1.13.1",
|
"jquery-ui-dist": "1.13.1",
|
||||||
|
@ -78,13 +77,9 @@
|
||||||
"ietf/static/js/edit-milestones.js",
|
"ietf/static/js/edit-milestones.js",
|
||||||
"ietf/static/js/edit_action_holders.js",
|
"ietf/static/js/edit_action_holders.js",
|
||||||
"ietf/static/js/edit_authors.js",
|
"ietf/static/js/edit_authors.js",
|
||||||
"ietf/static/js/flot.js",
|
|
||||||
"ietf/static/js/fullcalendar.js",
|
"ietf/static/js/fullcalendar.js",
|
||||||
"ietf/static/js/highcharts-export-data.js",
|
"ietf/static/js/highcharts.js",
|
||||||
"ietf/static/js/highcharts-exporting.js",
|
"ietf/static/js/highstock.js",
|
||||||
"ietf/static/js/highcharts-highcharts.js",
|
|
||||||
"ietf/static/js/highcharts-highstock.js",
|
|
||||||
"ietf/static/js/highcharts-offline-exporting.js",
|
|
||||||
"ietf/static/js/ietf.js",
|
"ietf/static/js/ietf.js",
|
||||||
"ietf/static/js/ipr-edit.js",
|
"ietf/static/js/ipr-edit.js",
|
||||||
"ietf/static/js/ipr-search.js",
|
"ietf/static/js/ipr-search.js",
|
||||||
|
@ -97,7 +92,6 @@
|
||||||
"ietf/static/js/meeting-interim-request.js",
|
"ietf/static/js/meeting-interim-request.js",
|
||||||
"ietf/static/js/moment.js",
|
"ietf/static/js/moment.js",
|
||||||
"ietf/static/js/password_strength.js",
|
"ietf/static/js/password_strength.js",
|
||||||
"ietf/static/js/review-stats.js",
|
|
||||||
"ietf/static/js/room_params.js",
|
"ietf/static/js/room_params.js",
|
||||||
"ietf/static/js/select2.js",
|
"ietf/static/js/select2.js",
|
||||||
"ietf/static/js/session_details_form.js",
|
"ietf/static/js/session_details_form.js",
|
||||||
|
|
|
@ -2729,13 +2729,6 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"flot@npm:4.2.2":
|
|
||||||
version: 4.2.2
|
|
||||||
resolution: "flot@npm:4.2.2"
|
|
||||||
checksum: 81910f7bc2b3808bcfc914c25d8c8119254b4401ce638ecef68568b29975bb22e25230f01cab9751c6a282b0b659b2a6d06d65f4b4d79257c47f2e966629f8b4
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"forever-agent@npm:~0.6.1":
|
"forever-agent@npm:~0.6.1":
|
||||||
version: 0.6.1
|
version: 0.6.1
|
||||||
resolution: "forever-agent@npm:0.6.1"
|
resolution: "forever-agent@npm:0.6.1"
|
||||||
|
@ -4377,7 +4370,6 @@ browserlist@latest:
|
||||||
cypress-real-events: 1.7.0
|
cypress-real-events: 1.7.0
|
||||||
d3: 7.4.4
|
d3: 7.4.4
|
||||||
eslint: 8.16.0
|
eslint: 8.16.0
|
||||||
flot: 4.2.2
|
|
||||||
highcharts: 10.1.0
|
highcharts: 10.1.0
|
||||||
html-validate: 7.1.1
|
html-validate: 7.1.1
|
||||||
jquery: 3.6.0
|
jquery: 3.6.0
|
||||||
|
|
Loading…
Reference in a new issue