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

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

Complexity

Total Complexity 52
Complexity/F 0

Size

Lines of Code 995
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 52
eloc 845
dl 0
loc 995
rs 7.195
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/CombinedEquipmentCost.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 SharePie from '../common/SharePie';
25
import { getCookieValue, createCookie } from '../../../helpers/utils';
26
import withRedirect from '../../../hoc/withRedirect';
27
import { withTranslation } from 'react-i18next';
28
import { toast } from 'react-toastify';
29
import ButtonIcon from '../../common/ButtonIcon';
30
import { APIBaseURL } from '../../../config';
31
import { periodTypeOptions } from '../common/PeriodTypeOptions';
32
import { comparisonTypeOptions } from '../common/ComparisonTypeOptions';
33
import DateRangePickerWrapper from '../common/DateRangePickerWrapper';
34
import { endOfDay} from 'date-fns';
35
import AppContext from '../../../context/Context';
36
import MultipleLineChart from '../common/MultipleLineChart';
37
38
const DetailedDataTable = loadable(() => import('../common/DetailedDataTable'));
39
const AssociatedEquipmentTable = loadable(() => import('../common/AssociatedEquipmentTable'));
40
41
const CombinedEquipmentCost = ({ 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 [costShareData, setCostShareData] = 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/combinedequipmentcost?' +
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 costDataArray = [];
425
        json['reporting_period']['names'].forEach((currentValue, index) => {
426
          let costDataItem = {}
427
          costDataItem['id'] = index;
428
          costDataItem['name'] = currentValue;
429
          costDataItem['value'] = json['reporting_period']['subtotals'][index];
430
          costDataItem['color'] = "#"+((1<<24)*Math.random()|0).toString(16);
431
          costDataArray.push(costDataItem);
432
        });
433
        setCostShareData(costDataArray);
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
          
521
          names.push({ 'value': 'a' + index, 'label': currentValue });
522
        });
523
        setParameterLineChartOptions(names);
524
525
        if(!isBasePeriodTimestampExists(json['base_period'])) {
526
          let detailed_value_list = [];
527
          if (json['reporting_period']['timestamps'].length > 0) {
528
            json['reporting_period']['timestamps'][0].forEach((currentTimestamp, timestampIndex) => {
529
              let detailed_value = {};
530
              detailed_value['id'] = timestampIndex;
531
              detailed_value['startdatetime'] = currentTimestamp;
532
              let total_current_timstamp = 0.0;
533
              json['reporting_period']['values'].forEach((currentValue, energyCategoryIndex) => {
534
                detailed_value['a' + energyCategoryIndex] = json['reporting_period']['values'][energyCategoryIndex][timestampIndex];
535
                total_current_timstamp += json['reporting_period']['values'][energyCategoryIndex][timestampIndex];
536
              });
537
              detailed_value['total'] = total_current_timstamp;
538
              detailed_value_list.push(detailed_value);
539
            });
540
          }
541
          ;
542
543
          let detailed_value = {};
544
          detailed_value['id'] = detailed_value_list.length;
545
          detailed_value['startdatetime'] = t('Subtotal');
546
          let total_of_subtotals = 0.0;
547
          json['reporting_period']['subtotals'].forEach((currentValue, index) => {
548
            detailed_value['a' + index] = currentValue;
549
            total_of_subtotals += currentValue
550
          });
551
          detailed_value['total'] = total_of_subtotals;
552
          detailed_value_list.push(detailed_value);
553
          setTimeout(() => {
554
            setDetailedDataTableData(detailed_value_list);
555
          }, 0)
556
557
          let detailed_column_list = [];
558
          detailed_column_list.push({
559
            dataField: 'startdatetime',
560
            text: t('Datetime'),
561
            sort: true
562
          });
563
          json['reporting_period']['names'].forEach((currentValue, index) => {
564
            let unit = json['reporting_period']['units'][index];
565
            detailed_column_list.push({
566
              dataField: 'a' + index,
567
              text: currentValue + ' (' + unit + ')',
568
              sort: true,
569
              formatter: function (decimalValue) {
570
                if (typeof decimalValue === 'number') {
571
                  return decimalValue.toFixed(2);
572
                } else {
573
                  return null;
574
                }
575
              }
576
            });
577
          });
578
          detailed_column_list.push({
579
            dataField: 'total',
580
            text: t('Total') + ' (' + json['reporting_period']['total_unit'] + ')',
581
            sort: true,
582
            formatter: function (decimalValue) {
583
              if (typeof decimalValue === 'number') {
584
                return decimalValue.toFixed(2);
585
              } else {
586
                return null;
587
              }
588
            }
589
          });
590
          setDetailedDataTableColumns(detailed_column_list);
591
        }else {
592
          /*
593
          * Tip:
594
          *     json['base_period']['names'] ===  json['reporting_period']['names']
595
          *     json['base_period']['units'] ===  json['reporting_period']['units']
596
          * */
597
          let detailed_column_list = [];
598
          detailed_column_list.push({
599
            dataField: 'basePeriodDatetime',
600
            text: t('Base Period') + ' - ' + t('Datetime'),
601
            sort: true
602
          })
603
604
          json['base_period']['names'].forEach((currentValue, index) => {
605
            let unit = json['base_period']['units'][index];
606
            detailed_column_list.push({
607
              dataField: 'a' + index,
608
              text: t('Base Period') + ' - ' + currentValue + ' (' + unit + ')',
609
              sort: true,
610
              formatter: function (decimalValue) {
611
                if (typeof decimalValue === 'number') {
612
                  return decimalValue.toFixed(2);
613
                } else {
614
                  return null;
615
                }
616
              }
617
            })
618
          });
619
620
          detailed_column_list.push({
621
            dataField: 'basePeriodTotal',
622
            text: t('Base Period') + ' - ' + t('Total') + ' (' + json['reporting_period']['total_unit'] + ')',
623
            sort: true,
624
            formatter: function (decimalValue) {
625
              if (typeof decimalValue === 'number') {
626
                return decimalValue.toFixed(2);
627
              } else {
628
                return null;
629
              }
630
            }
631
          })
632
633
          detailed_column_list.push({
634
            dataField: 'reportingPeriodDatetime',
635
            text: t('Reporting Period') + ' - ' + t('Datetime'),
636
            sort: true
637
          })
638
639
          json['reporting_period']['names'].forEach((currentValue, index) => {
640
            let unit = json['reporting_period']['units'][index];
641
            detailed_column_list.push({
642
              dataField: 'b' + index,
643
              text: t('Reporting Period') + ' - ' + currentValue + ' (' + unit + ')',
644
              sort: true,
645
              formatter: function (decimalValue) {
646
                if (typeof decimalValue === 'number') {
647
                  return decimalValue.toFixed(2);
648
                } else {
649
                  return null;
650
                }
651
              }
652
            })
653
          });
654
655
          detailed_column_list.push({
656
            dataField: 'reportingPeriodTotal',
657
            text: t('Reporting Period') + ' - ' + t('Total') + ' (' + json['reporting_period']['total_unit'] + ')',
658
            sort: true,
659
            formatter: function (decimalValue) {
660
              if (typeof decimalValue === 'number') {
661
                return decimalValue.toFixed(2);
662
              } else {
663
                return null;
664
              }
665
            }
666
          })
667
668
          setDetailedDataTableColumns(detailed_column_list);
669
670
          let detailed_value_list = [];
671
          if (json['base_period']['timestamps'].length > 0 || json['reporting_period']['timestamps'].length > 0) {
672
            const max_timestamps_length = json['base_period']['timestamps'][0].length >= json['reporting_period']['timestamps'][0].length?
673
                json['base_period']['timestamps'][0].length : json['reporting_period']['timestamps'][0].length;
674
            for (let index = 0; index < max_timestamps_length; index++) {
675
              let detailed_value = {};
676
              detailed_value['id'] = index;
677
              detailed_value['basePeriodDatetime'] = index < json['base_period']['timestamps'][0].length? json['base_period']['timestamps'][0][index] : null;
678
              detailed_value['basePeriodTotal'] = 0.0;
679
              if (detailed_value['basePeriodDatetime'] == null) {
680
                detailed_value['basePeriodTotal'] = null;
681
              }
682
              json['base_period']['values'].forEach((currentValue, energyCategoryIndex) => {
683
                detailed_value['a' + energyCategoryIndex] = index < json['base_period']['values'][energyCategoryIndex].length? json['base_period']['values'][energyCategoryIndex][index] : null;
684
                if(detailed_value['a' + energyCategoryIndex] != null) {
685
                  detailed_value['basePeriodTotal'] += detailed_value['a' + energyCategoryIndex];
686
                }
687
              });
688
              detailed_value['reportingPeriodDatetime'] = index < json['reporting_period']['timestamps'][0].length? json['reporting_period']['timestamps'][0][index] : null;
689
              detailed_value['reportingPeriodTotal'] = 0.0;
690
              if (detailed_value['reportingPeriodDatetime'] == null) {
691
                detailed_value['reportingPeriodTotal'] = null;
692
              }
693
              json['reporting_period']['values'].forEach((currentValue, energyCategoryIndex) => {
694
                detailed_value['b' + energyCategoryIndex] = index < json['reporting_period']['values'][energyCategoryIndex].length? json['reporting_period']['values'][energyCategoryIndex][index] : null;
695
                if(detailed_value['b' + energyCategoryIndex] != null) {
696
                  detailed_value['reportingPeriodTotal'] += detailed_value['b' + energyCategoryIndex];
697
                }
698
              });
699
              detailed_value_list.push(detailed_value);
700
            }
701
702
            let detailed_value = {};
703
            detailed_value['id'] = detailed_value_list.length;
704
            detailed_value['basePeriodDatetime'] = t('Subtotal');
705
            let total_of_subtotals_from_base_period = 0.0
706
            json['base_period']['subtotals'].forEach((currentValue, index) => {
707
              detailed_value['a' + index] = currentValue;
708
              total_of_subtotals_from_base_period += detailed_value['a' + index];
709
            });
710
            detailed_value['basePeriodTotal'] = total_of_subtotals_from_base_period;
711
712
            let total_of_subtotals_from_reporting_period = 0.0
713
            detailed_value['reportingPeriodDatetime'] = t('Subtotal');
714
            json['reporting_period']['subtotals'].forEach((currentValue, index) => {
715
              detailed_value['b' + index] = currentValue;
716
              total_of_subtotals_from_reporting_period += detailed_value['b' + index];
717
            });
718
            detailed_value['reportingPeriodTotal'] = total_of_subtotals_from_reporting_period;
719
            detailed_value_list.push(detailed_value);
720
            setTimeout( () => {
721
              setDetailedDataTableData(detailed_value_list);
722
            }, 0)
723
          }
724
        }
725
        
726
        let associated_equipment_value_list = [];
727
        if (json['associated_equipment']['associated_equipment_names_array'].length > 0) {
728
          json['associated_equipment']['associated_equipment_names_array'][0].forEach((currentEquipmentName, equipmentIndex) => {
729
            let associated_equipment_value = {};
730
            associated_equipment_value['id'] = equipmentIndex;
731
            associated_equipment_value['name'] = currentEquipmentName;
732
            let total = 0.0;
733
            json['associated_equipment']['energy_category_names'].forEach((currentValue, energyCategoryIndex) => {
734
              associated_equipment_value['a' + energyCategoryIndex] = json['associated_equipment']['subtotals_array'][energyCategoryIndex][equipmentIndex];
735
              total += json['associated_equipment']['subtotals_array'][energyCategoryIndex][equipmentIndex]
736
            });
737
            associated_equipment_value['total'] = total;
738
            associated_equipment_value_list.push(associated_equipment_value);
739
          });
740
        };
741
742
        setAssociatedEquipmentTableData(associated_equipment_value_list);
743
744
        let associated_equipment_column_list = [];
745
        associated_equipment_column_list.push({
746
          dataField: 'name',
747
          text: t('Associated Equipment'),
748
          sort: true
749
        });
750
        json['associated_equipment']['energy_category_names'].forEach((currentValue, index) => {
751
          let unit = json['associated_equipment']['units'][index];
752
          associated_equipment_column_list.push({
753
            dataField: 'a' + index,
754
            text: currentValue + ' (' + unit + ')',
755
            sort: true,
756
            formatter: function (decimalValue) {
757
              if (typeof decimalValue === 'number') {
758
                return decimalValue.toFixed(2);
759
              } else {
760
                return null;
761
              }
762
            }
763
          });
764
        });
765
        associated_equipment_column_list.push({
766
          dataField: 'total',
767
          text: t('Total') + ' (' + json['associated_equipment']['total_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
        setAssociatedEquipmentTableColumns(associated_equipment_column_list);
779
        
780
        setExcelBytesBase64(json['excel_bytes_base64']);
781
782
        // enable submit button
783
        setSubmitButtonDisabled(false);
784
        // hide spinner
785
        setSpinnerHidden(true);
786
        // show export button
787
        setExportButtonHidden(false);
788
      } else {
789
        toast.error(t(json.description))
790
      }
791
    }).catch(err => {
792
      console.log(err);
793
    });
794
  };
795
796
  const handleExport = e => {
797
    e.preventDefault();
798
    const mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
799
    const fileName = 'combinedequipmentcost.xlsx'
800
    var fileUrl = "data:" + mimeType + ";base64," + excelBytesBase64;
801
    fetch(fileUrl)
802
        .then(response => response.blob())
803
        .then(blob => {
804
            var link = window.document.createElement("a");
805
            link.href = window.URL.createObjectURL(blob, { type: mimeType });
806
            link.download = fileName;
807
            document.body.appendChild(link);
808
            link.click();
809
            document.body.removeChild(link);
810
        });
811
  };
812
813
  return (
814
    <Fragment>
815
      <div>
816
        <Breadcrumb>
817
          <BreadcrumbItem>{t('Combined Equipment Data')}</BreadcrumbItem><BreadcrumbItem active>{t('Cost')}</BreadcrumbItem>
818
        </Breadcrumb>
819
      </div>
820
      <Card className="bg-light mb-3">
821
        <CardBody className="p-3">
822
          <Form onSubmit={handleSubmit}>
823
            <Row form>
824
              <Col xs={6} sm={3}>
825
                <FormGroup className="form-group">
826
                  <Label className={labelClasses} for="space">
827
                    {t('Space')}
828
                  </Label>
829
                  <br />
830
                  <Cascader options={cascaderOptions}
831
                    onChange={onSpaceCascaderChange}
832
                    changeOnSelect
833
                    expandTrigger="hover">
834
                    <Input value={selectedSpaceName || ''} readOnly />
835
                  </Cascader>
836
                </FormGroup>
837
              </Col>
838
              <Col xs="auto">
839
                <FormGroup>
840
                  <Label className={labelClasses} for="combinedEquipmentSelect">
841
                    {t('Combined Equipment')}
842
                  </Label>
843
                  <CustomInput type="select" id="combinedEquipmentSelect" name="combinedEquipmentSelect" onChange={({ target }) => setSelectedCombinedEquipment(target.value)}
844
                  >
845
                    {combinedEquipmentList.map((combinedEquipment, index) => (
846
                      <option value={combinedEquipment.value} key={combinedEquipment.value}>
847
                        {combinedEquipment.label}
848
                      </option>
849
                    ))}
850
                  </CustomInput>
851
                </FormGroup>
852
              </Col>
853
              <Col xs="auto">
854
                <FormGroup>
855
                  <Label className={labelClasses} for="comparisonType">
856
                    {t('Comparison Types')}
857
                  </Label>
858
                  <CustomInput type="select" id="comparisonType" name="comparisonType"
859
                    defaultValue="month-on-month"
860
                    onChange={onComparisonTypeChange}
861
                  >
862
                    {comparisonTypeOptions.map((comparisonType, index) => (
863
                      <option value={comparisonType.value} key={comparisonType.value} >
864
                        {t(comparisonType.label)}
865
                      </option>
866
                    ))}
867
                  </CustomInput>
868
                </FormGroup>
869
              </Col>
870
              <Col xs="auto">
871
                <FormGroup>
872
                  <Label className={labelClasses} for="periodType">
873
                    {t('Period Types')}
874
                  </Label>
875
                  <CustomInput type="select" id="periodType" name="periodType" defaultValue="daily" onChange={({ target }) => setPeriodType(target.value)}
876
                  >
877
                    {periodTypeOptions.map((periodType, index) => (
878
                      <option value={periodType.value} key={periodType.value} >
879
                        {t(periodType.label)}
880
                      </option>
881
                    ))}
882
                  </CustomInput>
883
                </FormGroup>
884
              </Col>
885
              <Col xs={6} sm={3}>
886
                <FormGroup className="form-group">
887
                  <Label className={labelClasses} for="basePeriodDateRangePicker">{t('Base Period')}{t('(Optional)')}</Label>
888
                  <DateRangePickerWrapper 
889
                    id='basePeriodDateRangePicker'
890
                    disabled={basePeriodDateRangePickerDisabled}
891
                    format="yyyy-MM-dd HH:mm:ss"
892
                    value={basePeriodDateRange}
893
                    onChange={onBasePeriodChange}
894
                    size="md"
895
                    style={dateRangePickerStyle}
896
                    onClean={onBasePeriodClean}
897
                    locale={dateRangePickerLocale}
898
                    placeholder={t("Select Date Range")}
899
                   />
900
                </FormGroup>
901
              </Col>
902
              <Col xs={6} sm={3}>
903
                <FormGroup className="form-group">
904
                  <Label className={labelClasses} for="reportingPeriodDateRangePicker">{t('Reporting Period')}</Label>
905
                  <br/>
906
                  <DateRangePickerWrapper
907
                    id='reportingPeriodDateRangePicker'
908
                    format="yyyy-MM-dd HH:mm:ss"
909
                    value={reportingPeriodDateRange}
910
                    onChange={onReportingPeriodChange}
911
                    size="md"
912
                    style={dateRangePickerStyle}
913
                    onClean={onReportingPeriodClean}
914
                    locale={dateRangePickerLocale}
915
                    placeholder={t("Select Date Range")}
916
                  />
917
                </FormGroup>
918
              </Col>
919
              <Col xs="auto">
920
                <FormGroup>
921
                  <br></br>
922
                  <ButtonGroup id="submit">
923
                    <Button color="success" disabled={submitButtonDisabled} >{t('Submit')}</Button>
924
                  </ButtonGroup>
925
                </FormGroup>
926
              </Col>
927
              <Col xs="auto">
928
                <FormGroup>
929
                  <br></br>
930
                  <Spinner color="primary" hidden={spinnerHidden}  />
931
                </FormGroup>
932
              </Col>
933
              <Col xs="auto">
934
                  <br></br>
935
                  <ButtonIcon icon="external-link-alt" transform="shrink-3 down-2" color="falcon-default" 
936
                  hidden={exportButtonHidden}
937
                  onClick={handleExport} >
938
                    {t('Export')}
939
                  </ButtonIcon>
940
              </Col>
941
            </Row>
942
          </Form>
943
        </CardBody>
944
      </Card>
945
      <div className="card-deck">
946
        {cardSummaryList.map(cardSummaryItem => (
947
          <CardSummary key={cardSummaryItem['name']}
948
            rate={cardSummaryItem['increment_rate']}
949
            title={t('Reporting Period Costs CATEGORY UNIT', { 'CATEGORY': cardSummaryItem['name'], 'UNIT': '(' + cardSummaryItem['unit'] + ')' })}
950
            color="success" >
951
            {cardSummaryItem['subtotal'] && <CountUp end={cardSummaryItem['subtotal']} duration={2} prefix="" separator="," decimal="." decimals={2} />}
952
          </CardSummary>
953
        ))}
954
      </div>
955
      <Row noGutters>
956
        <Col className="mb-3 pr-lg-2 mb-3">
957
          <SharePie data={timeOfUseShareData} title={t('Electricity Cost by Time-Of-Use')} />
958
        </Col>
959
        <Col className="mb-3 pr-lg-2 mb-3">
960
          <SharePie data={costShareData} title={t('Costs by Energy Category')} />
961
        </Col>
962
      </Row>
963
964
      <MultiTrendChart reportingTitle = {{"name": "Reporting Period Costs CATEGORY VALUE UNIT", "substitute": ["CATEGORY", "VALUE", "UNIT"], "CATEGORY": combinedEquipmentBaseAndReportingNames, "VALUE": combinedEquipmentReportingSubtotals, "UNIT": combinedEquipmentBaseAndReportingUnits}}
965
        baseTitle = {{"name": "Base Period Costs CATEGORY VALUE UNIT", "substitute": ["CATEGORY", "VALUE", "UNIT"], "CATEGORY": combinedEquipmentBaseAndReportingNames, "VALUE": combinedEquipmentBaseSubtotals, "UNIT": combinedEquipmentBaseAndReportingUnits}}
966
        reportingTooltipTitle = {{"name": "Reporting Period Costs CATEGORY VALUE UNIT", "substitute": ["CATEGORY", "VALUE", "UNIT"], "CATEGORY": combinedEquipmentBaseAndReportingNames, "VALUE": null, "UNIT": combinedEquipmentBaseAndReportingUnits}}
967
        baseTooltipTitle = {{"name": "Base Period Costs CATEGORY VALUE UNIT", "substitute": ["CATEGORY", "VALUE", "UNIT"], "CATEGORY": combinedEquipmentBaseAndReportingNames, "VALUE": null, "UNIT": combinedEquipmentBaseAndReportingUnits}}
968
        reportingLabels={combinedEquipmentReportingLabels}
969
        reportingData={combinedEquipmentReportingData}
970
        baseLabels={combinedEquipmentBaseLabels}
971
        baseData={combinedEquipmentBaseData}
972
        rates={combinedEquipmentReportingRates}
973
        options={combinedEquipmentReportingOptions}>
974
      </MultiTrendChart>
975
976
      <MultipleLineChart reportingTitle={t('Related Parameters')}
977
        baseTitle=''
978
        labels={parameterLineChartLabels}
979
        data={parameterLineChartData}
980
        options={parameterLineChartOptions}>
981
      </MultipleLineChart>
982
983
      <br />
984
      <DetailedDataTable data={detailedDataTableData} title={t('Detailed Data')} columns={detailedDataTableColumns} pagesize={50} >
985
      </DetailedDataTable>
986
      <br />
987
      <AssociatedEquipmentTable data={associatedEquipmentTableData} title={t('Associated Equipment Data')} columns={associatedEquipmentTableColumns}>
988
      </AssociatedEquipmentTable>
989
990
    </Fragment>
991
  );
992
};
993
994
export default withTranslation()(withRedirect(CombinedEquipmentCost));
995