Passed
Push — master ( 02289f...d6a19b )
by Guangyu
08:58 queued 15s
created

myems-web/src/components/MyEMS/Space/SpaceCarbon.js   B

Complexity

Total Complexity 48
Complexity/F 0

Size

Lines of Code 969
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 48
eloc 826
dl 0
loc 969
rs 8.334
c 0
b 0
f 0
mnd 48
bc 48
fnc 0
bpm 0
cpm 0
noi 0

How to fix   Complexity   

Complexity

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