@ppci-mock/allocation-graph
Version:
Allocation graph
295 lines (258 loc) • 8.36 kB
JavaScript
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 =${() => { this.changeView(view, nextMoment); }}></li>
<li>${currentMoment && currentMoment.format(views[view])}</li>
<li =${() => { 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'} =${() => { 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);