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

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

Complexity

Total Complexity 53
Complexity/F 0

Size

Lines of Code 1006
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 53
eloc 854
mnd 53
bc 53
fnc 0
dl 0
loc 1006
rs 6.706
bpm 0
cpm 0
noi 0
c 0
b 0
f 0

How to fix   Complexity   

Complexity

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