Passed
Push — master ( 4d6f13...ccf8db )
by
unknown
11:59
created

myems-web/src/components/MyEMS/Equipment/EquipmentComparison.js   B

Complexity

Total Complexity 42
Complexity/F 0

Size

Lines of Code 1024
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 42
eloc 875
dl 0
loc 1024
rs 8.765
c 0
b 0
f 0
mnd 42
bc 42
fnc 0
bpm 0
cpm 0
noi 0

How to fix   Complexity   

Complexity

Complex classes like myems-web/src/components/MyEMS/Equipment/EquipmentComparison.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import React, { Fragment, useEffect, useState, useContext } from 'react';
2
import {
3
  Breadcrumb,
4
  BreadcrumbItem,
5
  Row,
6
  Col,
7
  Card,
8
  CardBody,
9
  Button,
10
  ButtonGroup,
11
  Form,
12
  FormGroup,
13
  Input,
14
  Label,
15
  CustomInput,
16
  Spinner
17
} from 'reactstrap';
18
import CountUp from 'react-countup';
19
import moment from 'moment';
20
import loadable from '@loadable/component';
21
import Cascader from 'rc-cascader';
22
import CardSummary from '../common/CardSummary';
23
import MultipleLineChart from '../common/MultipleLineChart';
24
import { getCookieValue, createCookie, checkEmpty } from '../../../helpers/utils';
25
import withRedirect from '../../../hoc/withRedirect';
26
import { withTranslation } from 'react-i18next';
27
import { toast } from 'react-toastify';
28
import ButtonIcon from '../../common/ButtonIcon';
29
import { APIBaseURL, settings } from '../../../config';
30
import { periodTypeOptions } from '../common/PeriodTypeOptions';
31
import MultiTrendChart from '../common/MultiTrendChart';
32
import DateRangePickerWrapper from '../common/DateRangePickerWrapper';
33
import { endOfDay } from 'date-fns';
34
import AppContext from '../../../context/Context';
35
import blankPage from '../../../assets/img/generic/blank-page.png';
36
37
const DetailedDataTable = loadable(() => import('../common/DetailedDataTable'));
38
39
const EquipmentComparison = ({ setRedirect, setRedirectUrl, t }) => {
40
  let current_moment = moment();
41
  useEffect(() => {
42
    let is_logged_in = getCookieValue('is_logged_in');
43
    let user_name = getCookieValue('user_name');
44
    let user_display_name = getCookieValue('user_display_name');
45
    let user_uuid = getCookieValue('user_uuid');
46
    let token = getCookieValue('token');
47
    if (checkEmpty(is_logged_in) || checkEmpty(token) || checkEmpty(user_uuid) || !is_logged_in) {
48
      setRedirectUrl(`/authentication/basic/login`);
49
      setRedirect(true);
50
    } else {
51
      //update expires time of cookies
52
      createCookie('is_logged_in', true, settings.cookieExpireTime);
53
      createCookie('user_name', user_name, settings.cookieExpireTime);
54
      createCookie('user_display_name', user_display_name, settings.cookieExpireTime);
55
      createCookie('user_uuid', user_uuid, settings.cookieExpireTime);
56
      createCookie('token', token, settings.cookieExpireTime);
57
    }
58
  });
59
60
  useEffect(() => {
61
    let timer = setInterval(() => {
62
      let is_logged_in = getCookieValue('is_logged_in');
63
      if (is_logged_in === null || !is_logged_in) {
64
        setRedirectUrl(`/authentication/basic/login`);
65
        setRedirect(true);
66
      }
67
    }, 1000);
68
    return () => clearInterval(timer);
69
  }, [setRedirect, setRedirectUrl]);
70
71
  // State
72
  //Query Form
73
  const [selectedSpaceName1, setSelectedSpaceName1] = useState(undefined);
74
  const [selectedSpaceName2, setSelectedSpaceName2] = useState(undefined);
75
  const [equipmentList1, setEquipmentList1] = useState([]);
76
  const [equipmentList2, setEquipmentList2] = useState([]);
77
  const [filteredEquipmentList1, setFilteredEquipmentList1] = useState([]);
78
  const [filteredEquipmentList2, setFilteredEquipmentList2] = useState([]);
79
  const [selectedEquipment1, setSelectedEquipment1] = useState(undefined);
80
  const [selectedEquipment2, setSelectedEquipment2] = useState(undefined);
81
  const [energyCategoryList, setEnergyCategoryList] = useState([]);
82
  const [selectedEnergyCategory, setSelectedEnergyCategory] = useState(undefined);
83
  const [periodType, setPeriodType] = useState('daily');
84
  const [cascaderOptions, setCascaderOptions] = useState(undefined);
85
  const [reportingPeriodDateRange, setReportingPeriodDateRange] = useState([
86
    current_moment
87
      .clone()
88
      .startOf('month')
89
      .toDate(),
90
    current_moment.toDate()
91
  ]);
92
  const dateRangePickerLocale = {
93
    sunday: t('sunday'),
94
    monday: t('monday'),
95
    tuesday: t('tuesday'),
96
    wednesday: t('wednesday'),
97
    thursday: t('thursday'),
98
    friday: t('friday'),
99
    saturday: t('saturday'),
100
    ok: t('ok'),
101
    today: t('today'),
102
    yesterday: t('yesterday'),
103
    hours: t('hours'),
104
    minutes: t('minutes'),
105
    seconds: t('seconds'),
106
    last7Days: t('last7Days'),
107
    formattedMonthPattern: 'yyyy-MM-dd'
108
  };
109
  const dateRangePickerStyle = { display: 'block', zIndex: 10 };
110
  const { language } = useContext(AppContext);
111
112
  // buttons
113
  const [submitButtonDisabled, setSubmitButtonDisabled] = useState(true);
114
  const [spinnerHidden, setSpinnerHidden] = useState(true);
115
  const [exportButtonHidden, setExportButtonHidden] = useState(true);
116
  const [resultDataHidden, setResultDataHidden] = useState(true);
117
  //Results
118
  const [equipment1, setEquipment1] = useState({
119
    id: undefined,
120
    name: undefined
121
  });
122
  const [equipment2, setEquipment2] = useState({
123
    id: undefined,
124
    name: undefined
125
  });
126
  const [energyCategory, setEnergyCategory] = useState({
127
    id: undefined,
128
    name: undefined,
129
    unit_of_measure: undefined
130
  });
131
  const [reportingPeriodEnergyConsumptionInCategory1, setReportingPeriodEnergyConsumptionInCategory1] = useState(0);
132
  const [reportingPeriodEnergyConsumptionInCategory2, setReportingPeriodEnergyConsumptionInCategory2] = useState(0);
133
  const [reportingPeriodEnergyConsumptionInDifference, setReportingPeriodEnergyConsumptionInDifference] = useState(0);
134
  const [equipmentLineChartData1, setEquipmentLineChartData1] = useState({ a0: [] });
135
  const [equipmentLineChartData2, setEquipmentLineChartData2] = useState({ a0: [] });
136
  const [equipmentLineChartLabels1, setEquipmentLineChartLabels1] = useState({ a0: [] });
137
  const [equipmentLineChartLabels2, setEquipmentLineChartLabels2] = useState({ a0: [] });
138
  const [parameterLineChartOptions, setParameterLineChartOptions] = useState([]);
139
  const [parameterLineChartData, setParameterLineChartData] = useState({});
140
  const [parameterLineChartLabels, setParameterLineChartLabels] = useState([]);
141
  const [detailedDataTableColumns, setDetailedDataTableColumns] = useState([
142
    { dataField: 'startdatetime', text: t('Datetime'), sort: true }
143
  ]);
144
  const [detailedDataTableData, setDetailedDataTableData] = useState([]);
145
  const [excelBytesBase64, setExcelBytesBase64] = useState(undefined);
146
147
  useEffect(() => {
148
    let isResponseOK = false;
149
    fetch(APIBaseURL + '/spaces/tree', {
150
      method: 'GET',
151
      headers: {
152
        'Content-type': 'application/json',
153
        'User-UUID': getCookieValue('user_uuid'),
154
        Token: getCookieValue('token')
155
      },
156
      body: null
157
    })
158
      .then(response => {
159
        if (response.ok) {
160
          isResponseOK = true;
161
        }
162
        return response.json();
163
      })
164
      .then(json => {
165
        if (isResponseOK) {
166
          // rename keys
167
          json = JSON.parse(
168
            JSON.stringify([json])
169
              .split('"id":')
170
              .join('"value":')
171
              .split('"name":')
172
              .join('"label":')
173
          );
174
          setCascaderOptions(json);
175
          setSelectedSpaceName1([json[0]].map(o => o.label));
176
          setSelectedSpaceName2([json[0]].map(o => o.label));
177
          let selectedSpaceID = [json[0]].map(o => o.value);
178
          // get Equipments by root Space ID
179
          let isResponseOK = false;
180
          fetch(APIBaseURL + '/spaces/' + selectedSpaceID + '/equipments', {
181
            method: 'GET',
182
            headers: {
183
              'Content-type': 'application/json',
184
              'User-UUID': getCookieValue('user_uuid'),
185
              Token: getCookieValue('token')
186
            },
187
            body: null
188
          })
189
            .then(response => {
190
              if (response.ok) {
191
                isResponseOK = true;
192
              }
193
              return response.json();
194
            })
195
            .then(json => {
196
              if (isResponseOK) {
197
                json = JSON.parse(
198
                  JSON.stringify([json])
199
                    .split('"id":')
200
                    .join('"value":')
201
                    .split('"name":')
202
                    .join('"label":')
203
                );
204
205
                setEquipmentList1(json[0]);
206
                setEquipmentList2(json[0]);
207
                setFilteredEquipmentList1(json[0]);
208
                setFilteredEquipmentList2(json[0]);
209
                if (json[0].length > 0) {
210
                  setSelectedEquipment1(json[0][0].value);
211
                  setSelectedEquipment2(json[0][0].value);
212
                } else {
213
                  setSelectedEquipment1(undefined);
214
                  setSelectedEquipment2(undefined);
215
                }
216
              } else {
217
                toast.error(t(json.description));
218
              }
219
            })
220
            .catch(err => {
221
              console.log(err);
222
            });
223
          // end of get Equipments by root Space ID
224
        } else {
225
          toast.error(t(json.description));
226
        }
227
      })
228
      .catch(err => {
229
        console.log(err);
230
      });
231
232
    // Load energy categories
233
    fetch(APIBaseURL + '/energycategories', {
234
      method: 'GET',
235
      headers: {
236
        'Content-type': 'application/json',
237
        'User-UUID': getCookieValue('user_uuid'),
238
        Token: getCookieValue('token')
239
      },
240
      body: null
241
    })
242
      .then(response => {
243
        if (response.ok) {
244
          isResponseOK = true;
245
        }
246
        return response.json();
247
      })
248
      .then(json => {
249
        if (isResponseOK) {
250
          json = JSON.parse(
251
            JSON.stringify([json])
252
              .split('"id":')
253
              .join('"value":')
254
              .split('"name":')
255
              .join('"label":')
256
          );
257
          setEnergyCategoryList(json[0]);
258
          if (json[0].length > 0) {
259
            setSelectedEnergyCategory(json[0][0].value);
260
            // enable submit button
261
            setSubmitButtonDisabled(false);
262
          } else {
263
            setSelectedEnergyCategory(undefined);
264
            // disable submit button
265
            setSubmitButtonDisabled(true);
266
          }
267
        } else {
268
          toast.error(t(json.description));
269
        }
270
      })
271
      .catch(err => {
272
        console.log(err);
273
      });
274
  }, [t]);
275
276
  const labelClasses = 'ls text-uppercase text-600 font-weight-semi-bold mb-0';
277
278
  let onSpaceCascaderChange1 = (value, selectedOptions) => {
279
    setSelectedSpaceName1(selectedOptions.map(o => o.label).join('/'));
280
    let selectedSpaceID = value[value.length - 1];
281
    let isResponseOK = false;
282
    fetch(APIBaseURL + '/spaces/' + selectedSpaceID + '/equipments', {
283
      method: 'GET',
284
      headers: {
285
        'Content-type': 'application/json',
286
        'User-UUID': getCookieValue('user_uuid'),
287
        Token: getCookieValue('token')
288
      },
289
      body: null
290
    })
291
      .then(response => {
292
        if (response.ok) {
293
          isResponseOK = true;
294
        }
295
        return response.json();
296
      })
297
      .then(json => {
298
        if (isResponseOK) {
299
          json = JSON.parse(
300
            JSON.stringify([json])
301
              .split('"id":')
302
              .join('"value":')
303
              .split('"name":')
304
              .join('"label":')
305
          );
306
307
          setEquipmentList1(json[0]);
308
          setFilteredEquipmentList1(json[0]);
309
          if (json[0].length > 0) {
310
            setSelectedEquipment1(json[0][0].value);
311
            // enable submit button
312
            setSubmitButtonDisabled(false);
313
          } else {
314
            setSelectedEquipment1(undefined);
315
            // disable submit button
316
            setSubmitButtonDisabled(true);
317
          }
318
        } else {
319
          toast.error(t(json.description));
320
        }
321
      })
322
      .catch(err => {
323
        console.log(err);
324
      });
325
  };
326
327
  let onSpaceCascaderChange2 = (value, selectedOptions) => {
328
    setSelectedSpaceName2(selectedOptions.map(o => o.label).join('/'));
329
    let selectedSpaceID = value[value.length - 1];
330
    let isResponseOK = false;
331
    fetch(APIBaseURL + '/spaces/' + selectedSpaceID + '/equipments', {
332
      method: 'GET',
333
      headers: {
334
        'Content-type': 'application/json',
335
        'User-UUID': getCookieValue('user_uuid'),
336
        Token: getCookieValue('token')
337
      },
338
      body: null
339
    })
340
      .then(response => {
341
        if (response.ok) {
342
          isResponseOK = true;
343
        }
344
        return response.json();
345
      })
346
      .then(json => {
347
        if (isResponseOK) {
348
          json = JSON.parse(
349
            JSON.stringify([json])
350
              .split('"id":')
351
              .join('"value":')
352
              .split('"name":')
353
              .join('"label":')
354
          );
355
356
          setEquipmentList2(json[0]);
357
          setFilteredEquipmentList2(json[0]);
358
          if (json[0].length > 0) {
359
            setSelectedEquipment2(json[0][0].value);
360
            // enable submit button
361
            setSubmitButtonDisabled(false);
362
          } else {
363
            setSelectedEquipment2(undefined);
364
            // disable submit button
365
            setSubmitButtonDisabled(true);
366
          }
367
        } else {
368
          toast.error(t(json.description));
369
        }
370
      })
371
      .catch(err => {
372
        console.log(err);
373
      });
374
  };
375
376
  const onSearchEquipment1 = ({ target }) => {
377
    const keyword = target.value.toLowerCase();
378
    const filteredResult = equipmentList1.filter(equipment => equipment.label.toLowerCase().includes(keyword));
379
    setFilteredEquipmentList1(keyword.length ? filteredResult : equipmentList1);
380
    if (filteredResult.length > 0) {
381
      setSelectedEquipment1(filteredResult[0].value);
382
      // enable submit button
383
      setSubmitButtonDisabled(false);
384
    } else {
385
      setSelectedEquipment1(undefined);
386
      // disable submit button
387
      setSubmitButtonDisabled(true);
388
    }
389
    let customInputTarget = document.getElementById('equipmentSelect1');
390
    customInputTarget.value = filteredResult[0].value;
391
  };
392
393
  const onSearchEquipment2 = ({ target }) => {
394
    const keyword = target.value.toLowerCase();
395
    const filteredResult = equipmentList2.filter(equipment => equipment.label.toLowerCase().includes(keyword));
396
    setFilteredEquipmentList2(keyword.length ? filteredResult : equipmentList2);
397
    if (filteredResult.length > 0) {
398
      setSelectedEquipment2(filteredResult[0].value);
399
      // enable submit button
400
      setSubmitButtonDisabled(false);
401
    } else {
402
      setSelectedEquipment2(undefined);
403
      // disable submit button
404
      setSubmitButtonDisabled(true);
405
    }
406
    let customInputTarget = document.getElementById('equipmentSelect2');
407
    customInputTarget.value = filteredResult[0].value;
408
  };
409
410
  // Callback fired when value changed
411
  let onReportingPeriodChange = DateRange => {
412
    if (DateRange == null) {
413
      setReportingPeriodDateRange([null, null]);
414
    } else {
415
      if (moment(DateRange[1]).format('HH:mm:ss') === '00:00:00') {
416
        // if the user did not change time value, set the default time to the end of day
417
        DateRange[1] = endOfDay(DateRange[1]);
418
      }
419
      setReportingPeriodDateRange([DateRange[0], DateRange[1]]);
420
      const dateDifferenceInSeconds = moment(DateRange[1]).valueOf() / 1000 - moment(DateRange[0]).valueOf() / 1000;
421
      if (periodType === 'hourly') {
422
        if (dateDifferenceInSeconds > 3 * 365 * 24 * 60 * 60) {
423
          // more than 3 years
424
          setPeriodType('yearly');
425
          document.getElementById('periodType').value = 'yearly';
426
        } else if (dateDifferenceInSeconds > 6 * 30 * 24 * 60 * 60) {
427
          // more than 6 months
428
          setPeriodType('monthly');
429
          document.getElementById('periodType').value = 'monthly';
430
        } else if (dateDifferenceInSeconds > 30 * 24 * 60 * 60) {
431
          // more than 30 days
432
          setPeriodType('daily');
433
          document.getElementById('periodType').value = 'daily';
434
        }
435
      } else if (periodType === 'daily') {
436
        if (dateDifferenceInSeconds >= 3 * 365 * 24 * 60 * 60) {
437
          // more than 3 years
438
          setPeriodType('yearly');
439
          document.getElementById('periodType').value = 'yearly';
440
        } else if (dateDifferenceInSeconds >= 6 * 30 * 24 * 60 * 60) {
441
          // more than 6 months
442
          setPeriodType('monthly');
443
          document.getElementById('periodType').value = 'monthly';
444
        }
445
      } else if (periodType === 'monthly') {
446
        if (dateDifferenceInSeconds >= 3 * 365 * 24 * 60 * 60) {
447
          // more than 3 years
448
          setPeriodType('yearly');
449
          document.getElementById('periodType').value = 'yearly';
450
        }
451
      }
452
    }
453
  };
454
455
  // Callback fired when value clean
456
  let onReportingPeriodClean = event => {
457
    setReportingPeriodDateRange([null, null]);
458
  };
459
460
  // Handler
461
  const handleSubmit = e => {
462
    e.preventDefault();
463
    // disable submit button
464
    setSubmitButtonDisabled(true);
465
    // show spinner
466
    setSpinnerHidden(false);
467
    // hide export button
468
    setExportButtonHidden(true);
469
    // hide result data
470
    setResultDataHidden(true);
471
472
    // Reinitialize tables
473
    setDetailedDataTableData([]);
474
475
    let isResponseOK = false;
476
    fetch(
477
      APIBaseURL +
478
        '/reports/equipmentcomparison?' +
479
        'equipmentid1=' +
480
        selectedEquipment1 +
481
        '&equipmentid2=' +
482
        selectedEquipment2 +
483
        '&energycategoryid=' +
484
        selectedEnergyCategory +
485
        '&periodtype=' +
486
        periodType +
487
        '&reportingperiodstartdatetime=' +
488
        moment(reportingPeriodDateRange[0]).format('YYYY-MM-DDTHH:mm:ss') +
489
        '&reportingperiodenddatetime=' +
490
        moment(reportingPeriodDateRange[1]).format('YYYY-MM-DDTHH:mm:ss') +
491
        '&language=' +
492
        language,
493
      {
494
        method: 'GET',
495
        headers: {
496
          'Content-type': 'application/json',
497
          'User-UUID': getCookieValue('user_uuid'),
498
          Token: getCookieValue('token')
499
        },
500
        body: null
501
      }
502
    )
503
      .then(response => {
504
        if (response.ok) {
505
          isResponseOK = true;
506
        }
507
        return response.json();
508
      })
509
      .then(json => {
510
        if (isResponseOK) {
511
          setEquipment1({
512
            id: json['equipment1']['id'],
513
            name: json['equipment1']['name']
514
          });
515
          setEquipment2({
516
            id: json['equipment2']['id'],
517
            name: json['equipment2']['name']
518
          });
519
          setEnergyCategory({
520
            id: json['energy_category']['id'],
521
            name: json['energy_category']['name'],
522
            unit_of_measure: json['energy_category']['unit_of_measure']
523
          });
524
          setReportingPeriodEnergyConsumptionInCategory1(json['reporting_period1']['total_in_category']);
525
          setReportingPeriodEnergyConsumptionInCategory2(json['reporting_period2']['total_in_category']);
526
          setReportingPeriodEnergyConsumptionInDifference(json['diff']['total_in_category']);
527
          let names1 = [];
528
          names1.push({ value: 'a0', label: json['energy_category']['name'] });
529
530
          let names2 = [];
531
          names2.push({ value: 'a0', label: json['energy_category']['name'] });
532
533
          let timestamps1 = {};
534
          timestamps1['a0'] = json['reporting_period1']['timestamps'];
535
          setEquipmentLineChartLabels1(timestamps1);
536
537
          let timestamps2 = {};
538
          timestamps2['a0'] = json['reporting_period2']['timestamps'];
539
          setEquipmentLineChartLabels2(timestamps2);
540
541
          let values1 = { a0: [] };
542
          json['reporting_period1']['values'].forEach((currentValue, index) => {
543
            values1['a0'][index] = currentValue === null ? null : currentValue.toFixed(2);
544
          });
545
          setEquipmentLineChartData1(values1);
546
547
          let values2 = { a0: [] };
548
          json['reporting_period2']['values'].forEach((currentValue, index) => {
549
            values2['a0'][index] = currentValue === null ? null : currentValue.toFixed(2);
550
          });
551
          setEquipmentLineChartData2(values2);
552
553
          names1 = [];
554
          let index1 = 0;
555
          json['parameters1']['names'].forEach((currentValue, index) => {
556
            names1.push({ 
557
              value: 'a' + index1, 
558
              label: json['equipment1']['name'] + ' ' + json['energy_category']['name'] + ' (' + json['energy_category']['unit_of_measure'] + ')'
559
            });
560
            index1 = index1 + 1;
561
          });
562
          json['parameters2']['names'].forEach((currentValue, index) => {
563
            names1.push({ 
564
              value: 'a' + index1, 
565
              label: json['equipment2']['name'] + ' ' + json['energy_category']['name'] + ' (' + json['energy_category']['unit_of_measure'] + ')'
566
            });
567
            index1 = index1 + 1;
568
          });
569
          setParameterLineChartOptions(names1);
570
571
          timestamps1 = {};
572
          index1 = 0;
573
          json['parameters1']['timestamps'].forEach((currentValue, index) => {
574
            timestamps1['a' + index1] = currentValue;
575
            index1 = index1 + 1;
576
          });
577
          json['parameters2']['timestamps'].forEach((currentValue, index) => {
578
            timestamps1['a' + index1] = currentValue;
579
            index1 = index1 + 1;
580
          });
581
          setParameterLineChartLabels(timestamps1);
582
583
          index1 = 0;
584
          let values = { a0: [] };
585
          json['parameters1']['values'].forEach((currentValue, index) => {
586
            values['a' + index1] = currentValue;
587
            index1 += 1;
588
          });
589
          json['parameters2']['values'].forEach((currentValue, index) => {
590
            values['a' + index1] = currentValue;
591
            index1 += 1;
592
          });
593
          setParameterLineChartData(values);
594
595
          setDetailedDataTableColumns([
596
            {
597
              dataField: 'startdatetime',
598
              text: t('Datetime'),
599
              sort: true
600
            },
601
            {
602
              dataField: 'a0',
603
              text:
604
                json['equipment1']['name'] +
605
                ' ' +
606
                json['energy_category']['name'] +
607
                ' (' +
608
                json['energy_category']['unit_of_measure'] +
609
                ')',
610
              sort: true,
611
              formatter: function(decimalValue) {
612
                if (typeof decimalValue === 'number') {
613
                  return decimalValue.toFixed(2);
614
                } else {
615
                  return null;
616
                }
617
              }
618
            },
619
            {
620
              dataField: 'a1',
621
              text:
622
                json['equipment2']['name'] +
623
                ' ' +
624
                json['energy_category']['name'] +
625
                ' (' +
626
                json['energy_category']['unit_of_measure'] +
627
                ')',
628
              sort: true,
629
              formatter: function(decimalValue) {
630
                if (typeof decimalValue === 'number') {
631
                  return decimalValue.toFixed(2);
632
                } else {
633
                  return null;
634
                }
635
              }
636
            },
637
            {
638
              dataField: 'a2',
639
              text: t('Reporting Period Difference CATEGORY UNIT', {
640
                CATEGORY: json['energy_category']['name'],
641
                UNIT: '(' + json['energy_category']['unit_of_measure'] + ')'
642
              }),
643
              sort: true,
644
              formatter: function(decimalValue) {
645
                if (typeof decimalValue === 'number') {
646
                  return decimalValue.toFixed(2);
647
                } else {
648
                  return null;
649
                }
650
              }
651
            }
652
          ]);
653
654
          let detailed_value_list = [];
655
          json['reporting_period1']['timestamps'].forEach((currentTimestamp, timestampIndex) => {
656
            let detailed_value = {};
657
            detailed_value['id'] = timestampIndex;
658
            detailed_value['startdatetime'] = currentTimestamp;
659
            detailed_value['a0'] = json['reporting_period1']['values'][timestampIndex];
660
            detailed_value['a1'] = json['reporting_period2']['values'][timestampIndex];
661
            detailed_value['a2'] = json['diff']['values'][timestampIndex];
662
            detailed_value_list.push(detailed_value);
663
          });
664
665
          let detailed_value = {};
666
          detailed_value['id'] = detailed_value_list.length;
667
          detailed_value['startdatetime'] = t('Total');
668
          detailed_value['a0'] = json['reporting_period1']['total_in_category'];
669
          detailed_value['a1'] = json['reporting_period2']['total_in_category'];
670
          detailed_value['a2'] = json['diff']['total_in_category'];
671
          detailed_value_list.push(detailed_value);
672
          setTimeout(() => {
673
            setDetailedDataTableData(detailed_value_list);
674
          }, 0);
675
676
          setExcelBytesBase64(json['excel_bytes_base64']);
677
678
          // enable submit button
679
          setSubmitButtonDisabled(false);
680
          // hide spinner
681
          setSpinnerHidden(true);
682
          // show export button
683
          setExportButtonHidden(false);
684
          // show result data
685
          setResultDataHidden(false);
686
        } else {
687
          toast.error(t(json.description));
688
          setSpinnerHidden(true);
689
          setSubmitButtonDisabled(false);
690
        }
691
      })
692
      .catch(err => {
693
        console.log(err);
694
      });
695
  };
696
697
  const handleExport = e => {
698
    e.preventDefault();
699
    const mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
700
    const fileName = 'equipmentcomparison.xlsx';
701
    var fileUrl = 'data:' + mimeType + ';base64,' + excelBytesBase64;
702
    fetch(fileUrl)
703
      .then(response => response.blob())
704
      .then(blob => {
705
        var link = window.document.createElement('a');
706
        link.href = window.URL.createObjectURL(blob, { type: mimeType });
707
        link.download = fileName;
708
        document.body.appendChild(link);
709
        link.click();
710
        document.body.removeChild(link);
711
      });
712
  };
713
714
  return (
715
    <Fragment>
716
      <div>
717
        <Breadcrumb>
718
          <BreadcrumbItem>{t('Equipment Data')}</BreadcrumbItem>
719
          <BreadcrumbItem active>{t('Equipment Comparison')}</BreadcrumbItem>
720
        </Breadcrumb>
721
      </div>
722
      <Card className="bg-light mb-3">
723
        <CardBody className="p-3">
724
          <Form onSubmit={handleSubmit}>
725
            <Row form>
726
              <Col xs={6} sm={3}>
727
                <FormGroup className="form-group">
728
                  <Label className={labelClasses} for="space">
729
                    {t('Space')}1
730
                  </Label>
731
                  <br />
732
                  <Cascader
733
                    options={cascaderOptions}
734
                    onChange={onSpaceCascaderChange1}
735
                    changeOnSelect
736
                    expandTrigger="hover"
737
                  >
738
                    <Input bsSize="sm" value={selectedSpaceName1 || ''} readOnly />
739
                  </Cascader>
740
                </FormGroup>
741
              </Col>
742
              <Col xs="auto">
743
                <FormGroup>
744
                  <Label className={labelClasses} for="equipmentSelect1">
745
                    {t('Equipment')}1
746
                  </Label>
747
748
                  <Form inline>
749
                    <Input placeholder={t('Search')} bsSize="sm" onChange={onSearchEquipment1} />
750
                    <CustomInput
751
                      type="select"
752
                      id="equipmentSelect1"
753
                      name="equipmentSelect1"
754
                      bsSize="sm"
755
                      onChange={({ target }) => setSelectedEquipment1(target.value)}
756
                    >
757
                      {filteredEquipmentList1.map((equipment, index) => (
758
                        <option value={equipment.value} key={equipment.value}>
759
                          {equipment.label}
760
                        </option>
761
                      ))}
762
                    </CustomInput>
763
                  </Form>
764
                </FormGroup>
765
              </Col>
766
            </Row>
767
            <Row form>
768
              <Col xs={6} sm={3}>
769
                <FormGroup className="form-group">
770
                  <Label className={labelClasses} for="space">
771
                    {t('Space')}2
772
                  </Label>
773
                  <br />
774
                  <Cascader
775
                    options={cascaderOptions}
776
                    onChange={onSpaceCascaderChange2}
777
                    changeOnSelect
778
                    expandTrigger="hover"
779
                  >
780
                    <Input value={selectedSpaceName2 || ''} readOnly />
781
                  </Cascader>
782
                </FormGroup>
783
              </Col>
784
              <Col xs="auto">
785
                <FormGroup>
786
                  <Label className={labelClasses} for="equipmentSelect2">
787
                    {t('Equipment')}2
788
                  </Label>
789
790
                  <Form inline>
791
                    <Input placeholder={t('Search')} bsSize="sm" onChange={onSearchEquipment2} />
792
                    <CustomInput
793
                      type="select"
794
                      id="equipmentSelect2"
795
                      name="equipmentSelect2"
796
                      bsSize="sm"
797
                      onChange={({ target }) => setSelectedEquipment2(target.value)}
798
                    >
799
                      {filteredEquipmentList2.map((equipment, index) => (
800
                        <option value={equipment.value} key={equipment.value}>
801
                          {equipment.label}
802
                        </option>
803
                      ))}
804
                    </CustomInput>
805
                  </Form>
806
                </FormGroup>
807
              </Col>
808
            </Row>
809
            <Row>
810
              <Col xs="auto">
811
                <FormGroup>
812
                  <Label className={labelClasses} for="energyCategorySelect">
813
                    {t('Energy Category')}
814
                  </Label>
815
                  <CustomInput
816
                    type="select"
817
                    id="energyCategorySelect"
818
                    name="energyCategorySelect"
819
                    bsSize="sm"
820
                    onChange={({ target }) => setSelectedEnergyCategory(target.value)}
821
                  >
822
                    {energyCategoryList.map((energyCategory, index) => (
823
                      <option value={energyCategory.value} key={energyCategory.value}>
824
                        {energyCategory.label}
825
                      </option>
826
                    ))}
827
                  </CustomInput>
828
                </FormGroup>
829
              </Col>
830
              <Col xs="auto">
831
                <FormGroup>
832
                  <Label className={labelClasses} for="periodType">
833
                    {t('Period Types')}
834
                  </Label>
835
                  <CustomInput
836
                    type="select"
837
                    id="periodType"
838
                    name="periodType"
839
                    bsSize="sm"
840
                    defaultValue="daily"
841
                    onChange={({ target }) => setPeriodType(target.value)}
842
                  >
843
                    {periodTypeOptions.map((periodType, index) => (
844
                      <option value={periodType.value} key={periodType.value}>
845
                        {t(periodType.label)}
846
                      </option>
847
                    ))}
848
                  </CustomInput>
849
                </FormGroup>
850
              </Col>
851
852
              <Col xs={6} sm={3}>
853
                <FormGroup className="form-group">
854
                  <Label className={labelClasses} for="reportingPeriodDateRangePicker">
855
                    {t('Reporting Period')}
856
                  </Label>
857
                  <br />
858
                  <DateRangePickerWrapper
859
                    id="reportingPeriodDateRangePicker"
860
                    format="yyyy-MM-dd HH:mm:ss"
861
                    value={reportingPeriodDateRange}
862
                    onChange={onReportingPeriodChange}
863
                    size="sm"
864
                    style={dateRangePickerStyle}
865
                    onClean={onReportingPeriodClean}
866
                    locale={dateRangePickerLocale}
867
                    placeholder={t('Select Date Range')}
868
                  />
869
                </FormGroup>
870
              </Col>
871
              <Col xs="auto">
872
                <FormGroup>
873
                  <br />
874
                  <ButtonGroup id="submit">
875
                    <Button size="sm" color="success" disabled={submitButtonDisabled}>
876
                      {t('Submit')}
877
                    </Button>
878
                  </ButtonGroup>
879
                </FormGroup>
880
              </Col>
881
              <Col xs="auto">
882
                <FormGroup>
883
                  <br />
884
                  <Spinner color="primary" hidden={spinnerHidden} />
885
                </FormGroup>
886
              </Col>
887
              <Col xs="auto">
888
                <br />
889
                <ButtonIcon
890
                  icon="external-link-alt"
891
                  transform="shrink-3 down-2"
892
                  color="falcon-default"
893
                  size="sm"
894
                  hidden={exportButtonHidden}
895
                  onClick={handleExport}
896
                >
897
                  {t('Export')}
898
                </ButtonIcon>
899
              </Col>
900
            </Row>
901
          </Form>
902
        </CardBody>
903
      </Card>
904
      <div className="blank-page-image-container" style={{ visibility: resultDataHidden ? 'visible' : 'hidden', display: resultDataHidden ? '' : 'none' }}>
905
        <img className="img-fluid" src={blankPage} alt="" />
906
      </div>
907
      <div style={{ visibility: resultDataHidden ? 'hidden' : 'visible', display: resultDataHidden ? 'none' : '' }}>
908
        <div className="card-deck">
909
          <CardSummary
910
            id="cardSummary1"
911
            title={t('EQUIPMENT CATEGORY VALUE UNIT', {
912
              EQUIPMENT: equipment1['name'],
913
              CATEGORY: energyCategory['name'],
914
              UNIT: '(' + energyCategory['unit_of_measure'] + ')'
915
            })}
916
            color="success"
917
          >
918
            <CountUp
919
              end={reportingPeriodEnergyConsumptionInCategory1}
920
              duration={2}
921
              prefix=""
922
              separator=","
923
              decimals={2}
924
              decimal="."
925
            />
926
          </CardSummary>
927
          <CardSummary
928
            id="cardSummary2"
929
            title={t('EQUIPMENT CATEGORY VALUE UNIT', {
930
              EQUIPMENT: equipment2['name'],
931
              CATEGORY: energyCategory['name'],
932
              UNIT: '(' + energyCategory['unit_of_measure'] + ')'
933
            })}
934
            color="success"
935
          >
936
            <CountUp
937
              end={reportingPeriodEnergyConsumptionInCategory2}
938
              duration={2}
939
              prefix=""
940
              separator=","
941
              decimals={2}
942
              decimal="."
943
            />
944
          </CardSummary>
945
          <CardSummary
946
            id="cardSummary3"
947
            title={t('Reporting Period Difference CATEGORY UNIT', {
948
              CATEGORY: energyCategory['name'],
949
              UNIT: '(' + energyCategory['unit_of_measure'] + ')'
950
            })}
951
            color="success"
952
          >
953
            <CountUp
954
              end={reportingPeriodEnergyConsumptionInDifference}
955
              duration={2}
956
              prefix=""
957
              separator=","
958
              decimals={2}
959
              decimal="."
960
            />
961
          </CardSummary>
962
        </div>
963
964
        <MultiTrendChart
965
          baseTitle={{
966
            name: 'EQUIPMENT CATEGORY VALUE UNIT',
967
            substitute: ['EQUIPMENT', 'CATEGORY', 'VALUE', 'UNIT'],
968
            EQUIPMENT: { a0: equipment1['name'] },
969
            CATEGORY: { a0: energyCategory['name'] },
970
            VALUE: { a0: reportingPeriodEnergyConsumptionInCategory1.toFixed(2) },
971
            UNIT: { a0: '(' + energyCategory['unit_of_measure'] + ')' }
972
          }}
973
          reportingTitle={{
974
            name: 'EQUIPMENT CATEGORY VALUE UNIT',
975
            substitute: ['EQUIPMENT', 'CATEGORY', 'VALUE', 'UNIT'],
976
            EQUIPMENT: { a0: equipment2['name'] },
977
            CATEGORY: { a0: energyCategory['name'] },
978
            VALUE: { a0: reportingPeriodEnergyConsumptionInCategory2.toFixed(2) },
979
            UNIT: { a0: '(' + energyCategory['unit_of_measure'] + ')' }
980
          }}
981
          baseTooltipTitle={{
982
            name: 'EQUIPMENT CATEGORY VALUE UNIT',
983
            substitute: ['EQUIPMENT', 'CATEGORY', 'VALUE', 'UNIT'],
984
            EQUIPMENT: { a0: equipment1['name'] },
985
            CATEGORY: { a0: energyCategory['name'] },
986
            VALUE: null,
987
            UNIT: { a0: '(' + energyCategory['unit_of_measure'] + ')' }
988
          }}
989
          reportingTooltipTitle={{
990
            name: 'EQUIPMENT CATEGORY VALUE UNIT',
991
            substitute: ['EQUIPMENT', 'CATEGORY', 'VALUE', 'UNIT'],
992
            EQUIPMENT: { a0: equipment2['name'] },
993
            CATEGORY: { a0: energyCategory['name'] },
994
            VALUE: null,
995
            UNIT: { a0: '(' + energyCategory['unit_of_measure'] + ')' }
996
          }}
997
          baseLabels={equipmentLineChartLabels1}
998
          baseData={equipmentLineChartData1}
999
          reportingLabels={equipmentLineChartLabels2}
1000
          reportingData={equipmentLineChartData2}
1001
          rates={{ a0: [] }}
1002
        />
1003
1004
        <MultipleLineChart
1005
          reportingTitle={t('Operating Characteristic Curve')}
1006
          baseTitle=""
1007
          labels={parameterLineChartLabels}
1008
          data={parameterLineChartData}
1009
          options={parameterLineChartOptions}
1010
        />
1011
        <br />
1012
        <DetailedDataTable
1013
          data={detailedDataTableData}
1014
          title={t('Detailed Data')}
1015
          columns={detailedDataTableColumns}
1016
          pagesize={50}
1017
        />
1018
      </div>
1019
    </Fragment>
1020
  );
1021
};
1022
1023
export default withTranslation()(withRedirect(EquipmentComparison));
1024