Passed
Push — master ( 414b30...be43ee )
by Guangyu
14:19 queued 13s
created

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

Complexity

Total Complexity 52
Complexity/F 0

Size

Lines of Code 994
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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