UNPKG

@ppci-mock/allocation-graph

Version:

Allocation graph

295 lines (258 loc) 8.36 kB
import { html } from 'lit-element'; import { ApolloQuery } from '@apollo-elements/lit-apollo'; import moment from 'moment'; /* Utils */ import { getClient, defaultNumberFormatter, LanguageMixin, getCssVariableValue, } from '@ppci-mock/utils'; import { query } from './graphql'; /* Styles */ import style from './style'; /* Components */ import '@ppci-mock/stacked-column-chart'; export class AllocationGraph extends LanguageMixin(ApolloQuery) { static get properties() { return { deviceId: { type: String }, direction: { type: String }, }; } constructor() { super(); this.loading = true; this.languageKey = 'allocationGraph'; this.fetchPolicy = 'no-cache'; this.client = getClient(); this.view = 0; this.numberFormatter = defaultNumberFormatter; } static get styles() { return style; } firstUpdated() { this.initTranslator(); this.fetch(); this.observableQuery.result().then((result) => { // Set current date from device endDate or current date if (!this.date) { const { startDate, endDate } = result.data.response.device; const maxDate = moment().subtract(1, 'days').format('YYYY-MM-DD'); this.date = endDate ? moment(endDate).max(maxDate).format('YYYY-MM-DD') : maxDate; this.min = moment(startDate).format('YYYY-MM-DD'); this.max = endDate ? moment(endDate).max(maxDate).format('YYYY-MM-DD') : maxDate; this.requestUpdate(); } }); } updated(changedProperties) { if ( changedProperties.get('deviceId') || changedProperties.get('direction') ) { this.fetch(); } } /* Override default Apollo Query should Update */ shouldUpdate() { return true; } fetch() { const { view, direction, client } = this; if (!client) return; const views = ['', 'year', 'month', 'day']; const aggregationType = ['YEARS', 'MONTHS', 'DAYS']; this.loading = true; this.variables = { direction, deviceId: this.deviceId, aggregationType: aggregationType[this.view], allocationBody: (data) => { const { device } = data.exportVariables; let startDate; let endDate; if (view === 0) { startDate = moment(device.startDate).format('YYYY-MM-DD'); endDate = device.endDate ? moment(device.endDate).format('YYYY-MM-DD') : moment().format('YYYY-MM-DD'); } else { const date = this.date || device.endDate || moment().subtract(1, 'days').format('YYYY-MM-DD'); startDate = moment(date, 'YYYY-MM-DD').startOf(views[view]).format('YYYY-MM-DD'); endDate = moment(date, 'YYYY-MM-DD').endOf(views[view]).format('YYYY-MM-DD'); } return { startDate, endDate, deviceId: this.deviceId, direction: this.direction.toUpperCase(), aggregationType: aggregationType[this.view], m2k: { ...window.powerpeers.mocks.allocationGraph, }, }; }, devicesBody: () => ({ m2k: { ...window.powerpeers.mocks.devices, }, }), }; this.query = query; this.requestUpdate(); } get renderLoader() { return html` <div class="loader-wrapper"> <custom-loader medium> </custom-loader> </div> `; } get allocation() { const { data } = this; const { response } = data || {}; const { allocation = [] } = response; const { content } = allocation[0] || []; return content; } changeView(view, newDate) { const { loading, date, min, max } = this; const views = ['year', 'month', 'day']; let currentMoment = newDate ? moment(newDate, 'YYYY-MM-DD') : moment(date, 'YYYY-MM-DD'); if (view < 0 || view > 2 || loading) return; if (currentMoment.isBefore(moment(min, 'YYYY-MM-DD').startOf(views[view]))) { if (newDate) return; currentMoment = moment(min, 'YYYY-MM-DD'); } if (currentMoment.isAfter(moment(max, 'YYYY-MM-DD').endOf(views[view]))) { if (newDate) return; currentMoment = moment(max, 'YYYY-MM-DD'); } this.view = view; this.date = currentMoment.format('YYYY-MM-DD'); this.fetch(); } get renderCalendar() { const { view, date } = this; const views = ['', 'YYYY', 'MMMM YYYY', 'D MMMM']; const next = ['', 'years', 'months', 'days']; const currentMoment = date && moment(date, 'YYYY-MM-DD'); const nextMoment = moment(currentMoment).subtract(1, next[view]).format('YYYY-MM-DD'); const prevMoment = moment(currentMoment).add(1, next[view]).format('YYYY-MM-DD'); if (view === 0) return null; return html` <ul class="calendar"> <li @click=${() => { this.changeView(view, nextMoment); }}></li> <li>${currentMoment && currentMoment.format(views[view])}</li> <li @click=${() => { this.changeView(view, prevMoment); }}></li> </ul> `; } get renderView() { const { translator } = this; const views = [ translator.t('rangeSelector.years'), translator.t('rangeSelector.months'), translator.t('rangeSelector.days'), ]; const items = views.map((v, i) => ( html`<li class=${i === this.view && 'active'} @click=${() => { this.changeView(i); }}>${v}</li>` )); return html` <ul class="view">${items}</ul> `; } get renderTotal() { const { translator } = this; const { allocation } = this; const total = allocation.records.totalValue; const formatted = this.numberFormatter.format(total) || 0; return html` <div class="total"> <strong>${translator.t('totalHeader', { total: `${formatted}` })}</strong> ${translator.t('common:energyUnit', { context: 'kWh' })} </div> `; } get renderGraph() { if (!this.data) return false; const { view, date, translator } = this; const names = ['YYYY', 'MMMM', 'D']; const graphSeries = [ { name: 'backup', label: translator.t('common:categories.backup'), color: getCssVariableValue('--cat-backup-color'), }, { name: 'water', label: translator.t('common:energySubTypes.water'), color: getCssVariableValue('--water-color'), }, { name: 'wind', label: translator.t('common:energySubTypes.wind'), color: getCssVariableValue('--wind-color'), }, { name: 'sun', label: translator.t('common:energySubTypes.sun'), color: getCssVariableValue('--sun-color'), }, ]; const { allocation } = this; const graphData = allocation.records.periods.map((p) => { const values = {}; const views = ['year', 'month', 'date']; const next = moment(p.period).get(views[view]); const newDate = moment(date, 'YYYY-MM-DD').set(views[view], next).format('YYYY-MM-DD'); graphSeries.forEach((serie) => { const { name } = serie; const array = p.types.filter((type) => { if (name === 'backup') return type.backup; return type.energyType.toLowerCase() === name; }); array.forEach((a) => { values[name] = a.value; }); }); return { onClick: () => { this.changeView(view + 1, newDate); }, name: moment(p.period).format(names[view]), opacity: p.final ? 1 : 0.5, values, }; }); return html` ${this.renderTotal} <stacked-column-chart .data=${graphData} .series=${graphSeries} ></stacked-column-chart> `; } render() { const { loading, error, translator } = this; const fetchError = error ? { title: translator.t('errors:defaultTitle'), message: error, retryTitle: translator.t('common:buttons.tryAgain'), retryAction: () => { this.error = null; this.query = query; }, } : null; return html` <widget-wrapper .error=${fetchError}> <div class="flex-wrapper"> <div class="tools"> ${this.renderView} ${this.renderCalendar} </div> ${loading ? this.renderLoader : this.renderGraph} </div> </widget-wrapper> `; } } customElements.define('allocation-graph', AllocationGraph);