Passed
Push — master ( ad5689...ea18a2 )
by Guangyu
08:17 queued 13s
created

myems-web/src/components/MyEMS/CombinedEquipment/CombinedEquipmentCarbon.js   C

Complexity

Total Complexity 53
Complexity/F 0

Size

Lines of Code 1005
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 53
eloc 853
mnd 53
bc 53
fnc 0
dl 0
loc 1005
rs 6.707
bpm 0
cpm 0
noi 0
c 0
b 0
f 0

How to fix   Complexity   

Complexity

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