1
|
|
|
import React, { useState, useContext, useEffect, useRef } from 'react'; |
2
|
|
|
import { Row, Col, Card, CardBody, CustomInput } from 'reactstrap'; |
3
|
|
|
import { rgbaColor, themeColors, isIterableArray } from '../../../helpers/utils'; |
4
|
|
|
import { |
5
|
|
|
Chart as ChartJS, |
6
|
|
|
CategoryScale, |
7
|
|
|
LinearScale, |
8
|
|
|
PointElement, |
9
|
|
|
BarElement, |
10
|
|
|
Tooltip, |
11
|
|
|
Legend, |
12
|
|
|
} from 'chart.js'; |
13
|
|
|
import { withTranslation } from 'react-i18next'; |
14
|
|
|
import { Chart } from 'react-chartjs-2'; |
15
|
|
|
import AppContext from '../../../context/Context'; |
16
|
|
|
import ChartDataLabels from 'chartjs-plugin-datalabels'; |
17
|
|
|
|
18
|
|
|
|
19
|
|
|
ChartJS.register( |
20
|
|
|
CategoryScale, |
21
|
|
|
LinearScale, |
22
|
|
|
PointElement, |
23
|
|
|
BarElement, |
24
|
|
|
Tooltip, |
25
|
|
|
Legend, |
26
|
|
|
); |
27
|
|
|
|
28
|
|
|
const MultiTrendChart = ({ |
29
|
|
|
reportingTitle, |
30
|
|
|
baseTitle, |
31
|
|
|
reportingTooltipTitle, |
32
|
|
|
baseTooltipTitle, |
33
|
|
|
reportingLabels, |
34
|
|
|
baseLabels, |
35
|
|
|
reportingData, |
36
|
|
|
baseData, |
37
|
|
|
rates, |
38
|
|
|
options, |
39
|
|
|
t |
40
|
|
|
}) => { |
41
|
|
|
const [selectedLabel, setSelectedLabel] = useState('a0'); |
42
|
|
|
const [option, setOption] = useState('a0'); |
43
|
|
|
const { isDark } = useContext(AppContext); |
44
|
|
|
const chartRef = useRef(null); |
45
|
|
|
const [lineData, setLineData] = useState({ |
46
|
|
|
datasets: [], |
47
|
|
|
}); |
48
|
|
|
useEffect(() => { |
49
|
|
|
const chart = chartRef.current; |
50
|
|
|
if (chart) { |
51
|
|
|
const ctx = chart.ctx; |
52
|
|
|
const gradientFill = isDark |
53
|
|
|
? ctx.createLinearGradient(0, 0, 0, ctx.canvas.height) |
54
|
|
|
: ctx.createLinearGradient(0, 0, 0, 250); |
55
|
|
|
gradientFill.addColorStop(0, isDark ? 'rgba(44,123,229, 0.5)' : 'rgba(255, 255, 255, 0.3)'); |
56
|
|
|
gradientFill.addColorStop(1, isDark ? 'transparent' : 'rgba(255, 255, 255, 0)'); |
57
|
|
|
|
58
|
|
|
const chartData = { |
59
|
|
|
datasets: [{ |
60
|
|
|
data: rates, |
61
|
|
|
borderColor: rgbaColor(isDark ? themeColors.primary : '#000', 0.8) , |
62
|
|
|
backgroundColor: gradientFill, |
63
|
|
|
type: 'line', |
64
|
|
|
yAxisID: 'y1', |
65
|
|
|
tooltip: { |
66
|
|
|
callbacks: { |
67
|
|
|
label: function(context){ |
68
|
|
|
return context.raw + '%'; |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
}, |
72
|
|
|
datalabels: { |
73
|
|
|
formatter: function(value, context) { |
74
|
|
|
return value + '%'; |
75
|
|
|
}, |
76
|
|
|
color: isDark ? themeColors.light : themeColors.dark, |
77
|
|
|
align: 'end', |
78
|
|
|
anchor: 'end', |
79
|
|
|
} |
80
|
|
|
},{ |
81
|
|
|
label: baseTitle, |
82
|
|
|
data: baseData[option], |
83
|
|
|
backgroundColor: '#4463b6', |
84
|
|
|
stack: "base", |
85
|
|
|
tension: 0.4, |
86
|
|
|
datalabels: { |
87
|
|
|
display: function(context){ |
88
|
|
|
return false; |
89
|
|
|
} |
90
|
|
|
}, |
91
|
|
|
},{ |
92
|
|
|
label: reportingTitle, |
93
|
|
|
data: reportingData[option], |
94
|
|
|
backgroundColor: '#e87637', |
95
|
|
|
stack: "reporting", |
96
|
|
|
tension: 0.4, |
97
|
|
|
datalabels: { |
98
|
|
|
display: function(context){ |
99
|
|
|
return false; |
100
|
|
|
} |
101
|
|
|
}, |
102
|
|
|
},], |
103
|
|
|
labels: reportingLabels[selectedLabel], |
104
|
|
|
}; |
105
|
|
|
setLineData(chartData); |
106
|
|
|
} |
107
|
|
|
}, [baseData, reportingData, option, baseLabels, reportingLabels, rates]); |
108
|
|
|
|
109
|
|
|
const config = { |
110
|
|
|
plugins: [ChartDataLabels], |
111
|
|
|
options: { |
112
|
|
|
plugins: { |
113
|
|
|
legend: { |
114
|
|
|
display: false, |
115
|
|
|
}, |
116
|
|
|
tooltip: { |
117
|
|
|
xPadding: 20, |
118
|
|
|
yPadding: 10, |
119
|
|
|
displayColors: false, |
120
|
|
|
callbacks: { |
121
|
|
|
title: function(context){ |
122
|
|
|
if (context[0].datasetIndex) { |
123
|
|
|
return `${reportingLabels[selectedLabel][context[0].dataIndex]}`; |
124
|
|
|
} else { |
125
|
|
|
return `${baseLabels[selectedLabel][context[0].dataIndex]}`; |
126
|
|
|
} |
127
|
|
|
}, |
128
|
|
|
label: function(context){ |
129
|
|
|
if (context.datasetIndex) { |
130
|
|
|
return `${reportingTooltipTitle} - ${context.raw}`; |
131
|
|
|
} else { |
132
|
|
|
return `${baseTooltipTitle} - ${context.raw}`; |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
}, |
137
|
|
|
}, |
138
|
|
|
interaction: { |
139
|
|
|
intersect: false, |
140
|
|
|
mode: 'x', |
141
|
|
|
}, |
142
|
|
|
scales: { |
143
|
|
|
x: { |
144
|
|
|
display: true, |
145
|
|
|
ticks: { |
146
|
|
|
fontColor: rgbaColor('#fff', 0.8), |
147
|
|
|
fontStyle: 600, |
148
|
|
|
color: isDark ? themeColors.light : themeColors.dark |
149
|
|
|
}, |
150
|
|
|
stacked: true, |
151
|
|
|
}, |
152
|
|
|
y: { |
153
|
|
|
display: true, |
154
|
|
|
gridLines: { |
155
|
|
|
color: rgbaColor('#000', 0.1) |
156
|
|
|
}, |
157
|
|
|
ticks: { |
158
|
|
|
color: isDark ? themeColors.light : themeColors.dark |
159
|
|
|
}, |
160
|
|
|
stack: true, |
161
|
|
|
}, |
162
|
|
|
y1: { |
163
|
|
|
type: 'linear', |
164
|
|
|
display: true, |
165
|
|
|
position: 'right', |
166
|
|
|
grid: { |
167
|
|
|
drawOnChartArea: false, |
168
|
|
|
}, |
169
|
|
|
suggestedMax: 100, |
170
|
|
|
ticks: { |
171
|
|
|
callback: function(value, index, ticks){ |
172
|
|
|
return value + '%'; |
173
|
|
|
}, |
174
|
|
|
color: isDark ? themeColors.light : themeColors.dark |
175
|
|
|
} |
176
|
|
|
}, |
177
|
|
|
}, |
178
|
|
|
hover: { mode: 'label' }, |
179
|
|
|
} |
180
|
|
|
}; |
181
|
|
|
|
182
|
|
|
return ( |
183
|
|
|
<Card className="mb-3"> |
184
|
|
|
<CardBody className="rounded-soft"> |
185
|
|
|
<Row className="text-white align-items-center no-gutters"> |
186
|
|
|
<Col> |
187
|
|
|
<h4 className="text-lightSlateGray mb-0">{reportingTitle}</h4> |
188
|
|
|
<p className="fs--1 font-weight-semi-bold"> |
189
|
|
|
{baseTitle} |
190
|
|
|
</p> |
191
|
|
|
</Col> |
192
|
|
|
{isIterableArray(options) && |
193
|
|
|
<Col xs="auto" className="d-none d-sm-block"> |
194
|
|
|
<CustomInput |
195
|
|
|
id="ddd" |
196
|
|
|
type="select" |
197
|
|
|
bsSize="sm" |
198
|
|
|
className="mb-3 shadow" |
199
|
|
|
value={option} |
200
|
|
|
onChange={({ target }) => {setOption(target.value); setSelectedLabel(target.value); chartRef.current.update();}} |
201
|
|
|
> |
202
|
|
|
{options.map(({ value, label }) => ( |
203
|
|
|
<option key={value} value={value}>{label}</option> |
204
|
|
|
))} |
205
|
|
|
</CustomInput> |
206
|
|
|
</Col> |
207
|
|
|
} |
208
|
|
|
</Row> |
209
|
|
|
<Chart ref={chartRef} type="bar" data={lineData} options={config.options} plugins={config.plugins} width={1618} height={218} /> |
210
|
|
|
</CardBody> |
211
|
|
|
</Card> |
212
|
|
|
); |
213
|
|
|
}; |
214
|
|
|
|
215
|
|
|
export default withTranslation()(MultiTrendChart); |
216
|
|
|
|
217
|
|
|
|