Passed
Push — master ( 7e7300...d38ed4 )
by Guangyu
09:06 queued 13s
created

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

Complexity

Total Complexity 41
Complexity/F 0

Size

Lines of Code 921
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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