Passed
Push — master ( 82dd0d...8c8679 )
by Guangyu
10:18 queued 13s
created

myems-web/src/components/MyEMS/Tenant/TenantCost.js   B

Complexity

Total Complexity 49
Complexity/F 0

Size

Lines of Code 936
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 49
eloc 795
dl 0
loc 936
rs 8.285
c 0
b 0
f 0
mnd 49
bc 49
fnc 0
bpm 0
cpm 0
noi 0

How to fix   Complexity   

Complexity

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