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

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

Complexity

Total Complexity 49
Complexity/F 0

Size

Lines of Code 1027
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 49
eloc 876
dl 0
loc 1027
rs 8.204
c 0
b 0
f 0
mnd 49
bc 49
fnc 0
bpm 0
cpm 0
noi 0

How to fix   Complexity   

Complexity

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

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

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