From bb10582fcc436a02c6797ce9035e3ba380fcb408 Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie <pierregilles.leymarie@gmail.com> Date: Thu, 11 Nov 2021 13:32:28 +0800 Subject: [PATCH 1/3] Add warning on chart box saying that Gladys needs time to collect enough data --- front/src/components/boxs/chart/Chart.jsx | 3 +++ front/src/components/boxs/chart/style.css | 10 +++++++++- front/src/config/i18n/en.json | 3 ++- front/src/config/i18n/fr.json | 5 +++-- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/front/src/components/boxs/chart/Chart.jsx b/front/src/components/boxs/chart/Chart.jsx index bbbf93b017..a2f1d18905 100644 --- a/front/src/components/boxs/chart/Chart.jsx +++ b/front/src/components/boxs/chart/Chart.jsx @@ -412,6 +412,9 @@ class Chartbox extends Component { <i class="fe fe-alert-circle mr-2" /> <Text id="dashboard.boxes.chart.noValue" /> </div> + <div class={style.smallTextEmptyState}> + <Text id="dashboard.boxes.chart.noValueWarning" /> + </div> </div> )} {emptySeries === false && !props.box.display_axes && ( diff --git a/front/src/components/boxs/chart/style.css b/front/src/components/boxs/chart/style.css index 7fa3bb2ed4..7070bacbf7 100644 --- a/front/src/components/boxs/chart/style.css +++ b/front/src/components/boxs/chart/style.css @@ -124,5 +124,13 @@ .bigEmptyState { margin-top: 0rem; - margin-bottom: 2.2rem; + margin-bottom: 1rem; +} + +.smallTextEmptyState { + margin-top: 1rem; + font-size: 12px; + padding-left: 1.5rem; + padding-right: 1.5rem; + text-align: justify; } diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index c3e75fae88..5a5f0f61e2 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -255,7 +255,8 @@ "lastThirtyDays": "Last 30 days", "lastThreeMonths": "Last 3 months", "lastYear": "Last year", - "noValue": "No values recorded on this interval", + "noValue": "No values recorded on this interval.", + "noValueWarning": "Warning: if you just configured this device, it may take some time before you see something here as Gladys needs some time to collect enough data. For interval superior to 24h, it may take up to 24h before you see something here.", "editNameLabel": "Enter the name of this box", "editDeviceFeaturesLabel": "Select the device you want to display here", "editRoomLabel": "Select the room you want to display here", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 9107b74e83..13f46e9f5e 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -250,12 +250,13 @@ "chart": { "defaultInterval": "Intervalle par défaut", "lastHour": "Dernière heure", - "lastDay": "Dernière 24h", + "lastDay": "Dernières 24h", "lastSevenDays": "Derniers 7 jours", "lastThirtyDays": "Derniers 30 jours", "lastThreeMonths": "Derniers 3 mois", "lastYear": "Dernière année", - "noValue": "Pas de valeurs sur cet interval", + "noValue": "Pas de valeurs sur cet intervalle.", + "noValueWarning": "Attention, si vous venez de configurer cet appareil, les données peuvent mettre un certain temps avant d'être aggrégées. Pour les intervalles supérieurs à 24h, cela prend jusqu'à 24h le temps que Gladys collecte assez de données.", "editNameLabel": "Entrez le nom de cette box", "editNamePlaceholder": "Nom affiché sur le tableau de bord", "editDeviceFeaturesLabel": "Sélectionnez les appareils que vous voulez afficher", From f5f0fe4ffd7e6d0f87c1fde6bedcea87ea42e1fd Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie <pierregilles.leymarie@gmail.com> Date: Thu, 11 Nov 2021 14:15:23 +0800 Subject: [PATCH 2/3] In chart, if two charts are displayed with different unit, display only the first + add units to legend --- front/src/components/boxs/chart/Chart.jsx | 38 ++++++++++++++++--- front/src/components/boxs/chart/EditChart.jsx | 13 +++++-- server/models/dashboard.js | 1 + 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/front/src/components/boxs/chart/Chart.jsx b/front/src/components/boxs/chart/Chart.jsx index a2f1d18905..602e247503 100644 --- a/front/src/components/boxs/chart/Chart.jsx +++ b/front/src/components/boxs/chart/Chart.jsx @@ -6,6 +6,7 @@ import { Text } from 'preact-i18n'; import style from './style.css'; import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants'; import get from 'get-value'; +import withIntlAsProp from '../../../utils/withIntlAsProp'; import ApexChartComponent from './ApexChartComponent'; const ONE_HOUR_IN_MINUTES = 60; @@ -57,6 +58,8 @@ const calculateVariation = (firstValue, lastValue) => { return Math.round(((lastValue - firstValue) / Math.abs(firstValue)) * 100); }; +const allEqual = arr => arr.every(val => val === arr[0]); + class Chartbox extends Component { toggleDropdown = () => { this.setState({ @@ -136,9 +139,12 @@ class Chartbox extends Component { let emptySeries = true; - const series = data.map(oneFeature => { + const series = data.map((oneFeature, index) => { + const oneUnit = this.props.box.units ? this.props.box.units[index] : this.props.box.unit; + const oneUnitTranslated = oneUnit ? this.props.intl.dictionary.deviceFeatureUnitShort[oneUnit] : null; + const name = oneUnitTranslated ? `${oneFeature.device.name} (${oneUnitTranslated})` : oneFeature.device.name; return { - name: oneFeature.device.name, + name, data: oneFeature.values.map(value => { emptySeries = false; return { @@ -155,7 +161,15 @@ class Chartbox extends Component { emptySeries }; - if (data.length > 0) { + // Before now, there was a "unit" attribute in this box instead of "units", + // so we need to support "unit" as some users may already have the box with that param + const unit = this.props.box.units ? this.props.box.units[0] : this.props.box.unit; + // We check if all deviceFeatures selected are in the same unit + const allUnitsAreSame = this.props.box.units ? allEqual(this.props.box.units) : false; + + // If all deviceFeatures selected are in the same unit + // We do a average of all values + if (data.length > 0 && allUnitsAreSame) { const lastValuesArray = []; const variationArray = []; data.forEach(oneFeature => { @@ -172,6 +186,18 @@ class Chartbox extends Component { }); newState.variation = average(variationArray); newState.lastValueRounded = roundWith2DecimalIfNeeded(average(lastValuesArray)); + newState.unit = unit; + } else if (data.length > 0) { + // If not, we only display the first value + const oneFeature = data[0]; + const { values } = oneFeature; + if (values.length > 0) { + const firstElement = values[0]; + const lastElement = values[values.length - 1]; + newState.variation = calculateVariation(firstElement.value, lastElement.value); + newState.lastValueRounded = roundWith2DecimalIfNeeded(lastElement.value); + newState.unit = unit; + } } await this.setState(newState); @@ -228,7 +254,7 @@ class Chartbox extends Component { this.updateDeviceStateWebsocket ); } - render(props, { loading, series, labels, dropdown, variation, lastValueRounded, interval, emptySeries }) { + render(props, { loading, series, labels, dropdown, variation, lastValueRounded, interval, emptySeries, unit }) { const displayVariation = props.box.display_variation; return ( <div class="card"> @@ -308,7 +334,7 @@ class Chartbox extends Component { {notNullNotUndefined(lastValueRounded) && !Number.isNaN(lastValueRounded) && ( <div class="h1 mb-0 mr-2"> {lastValueRounded} - {props.box.unit !== undefined && <Text id={`deviceFeatureUnitShort.${props.box.unit}`} />} + {unit !== undefined && <Text id={`deviceFeatureUnitShort.${unit}`} />} </div> )} <div @@ -434,4 +460,4 @@ class Chartbox extends Component { } } -export default connect('httpClient,session,user')(Chartbox); +export default withIntlAsProp(connect('httpClient,session,user')(Chartbox)); diff --git a/front/src/components/boxs/chart/EditChart.jsx b/front/src/components/boxs/chart/EditChart.jsx index cc9bff180f..9d809614cb 100644 --- a/front/src/components/boxs/chart/EditChart.jsx +++ b/front/src/components/boxs/chart/EditChart.jsx @@ -67,14 +67,19 @@ class EditChart extends Component { const deviceFeaturesSelectors = selectedDeviceFeaturesOptions.map( selectedDeviceFeaturesOption => selectedDeviceFeaturesOption.value ); - const firstDeviceFeature = this.deviceFeatureBySelector.get(selectedDeviceFeaturesOptions[0].value); + const units = selectedDeviceFeaturesOptions.map(selectedDeviceFeaturesOption => { + const deviceFeature = this.deviceFeatureBySelector.get(selectedDeviceFeaturesOption.value); + return deviceFeature.unit; + }); this.props.updateBoxConfig(this.props.x, this.props.y, { device_features: deviceFeaturesSelectors, - unit: firstDeviceFeature && firstDeviceFeature.unit ? firstDeviceFeature.unit : undefined + units, + unit: undefined }); } else { this.props.updateBoxConfig(this.props.x, this.props.y, { device_features: [], + units: [], unit: undefined }); } @@ -138,8 +143,8 @@ class EditChart extends Component { componentDidUpdate(previousProps) { const deviceFeatureChanged = get(previousProps, 'box.device_feature') !== get(this.props, 'box.device_feature'); - const unitChanged = get(previousProps, 'box.unit') !== get(this.props, 'box.unit'); - if (deviceFeatureChanged || unitChanged) { + const unitsChanged = get(previousProps, 'box.units') !== get(this.props, 'box.units'); + if (deviceFeatureChanged || unitsChanged) { this.getDeviceFeatures(); } } diff --git a/server/models/dashboard.js b/server/models/dashboard.js index edd9dc09b0..3c0ab41e98 100644 --- a/server/models/dashboard.js +++ b/server/models/dashboard.js @@ -16,6 +16,7 @@ const boxesSchema = Joi.array().items( device_features: Joi.array().items(Joi.string()), device_feature: Joi.string(), unit: Joi.string(), + units: Joi.array().items(Joi.string()), title: Joi.string(), interval: Joi.string(), display_axes: Joi.boolean(), From a2186ffa02054929ad9e7be6d9c2dd1228774a6e Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie <pierregilles.leymarie@gmail.com> Date: Thu, 11 Nov 2021 16:57:23 +0800 Subject: [PATCH 3/3] Separate the if --- front/src/components/boxs/chart/Chart.jsx | 68 ++++++++++++----------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/front/src/components/boxs/chart/Chart.jsx b/front/src/components/boxs/chart/Chart.jsx index 602e247503..acdf0721d4 100644 --- a/front/src/components/boxs/chart/Chart.jsx +++ b/front/src/components/boxs/chart/Chart.jsx @@ -161,42 +161,44 @@ class Chartbox extends Component { emptySeries }; - // Before now, there was a "unit" attribute in this box instead of "units", - // so we need to support "unit" as some users may already have the box with that param - const unit = this.props.box.units ? this.props.box.units[0] : this.props.box.unit; - // We check if all deviceFeatures selected are in the same unit - const allUnitsAreSame = this.props.box.units ? allEqual(this.props.box.units) : false; + if (data.length > 0) { + // Before now, there was a "unit" attribute in this box instead of "units", + // so we need to support "unit" as some users may already have the box with that param + const unit = this.props.box.units ? this.props.box.units[0] : this.props.box.unit; + // We check if all deviceFeatures selected are in the same unit + const allUnitsAreSame = this.props.box.units ? allEqual(this.props.box.units) : false; - // If all deviceFeatures selected are in the same unit - // We do a average of all values - if (data.length > 0 && allUnitsAreSame) { - const lastValuesArray = []; - const variationArray = []; - data.forEach(oneFeature => { + // If all deviceFeatures selected are in the same unit + // We do a average of all values + if (allUnitsAreSame) { + const lastValuesArray = []; + const variationArray = []; + data.forEach(oneFeature => { + const { values } = oneFeature; + if (values.length === 0) { + return; + } + const firstElement = values[0]; + const lastElement = values[values.length - 1]; + const variation = calculateVariation(firstElement.value, lastElement.value); + const lastValue = lastElement.value; + variationArray.push(variation); + lastValuesArray.push(lastValue); + }); + newState.variation = average(variationArray); + newState.lastValueRounded = roundWith2DecimalIfNeeded(average(lastValuesArray)); + newState.unit = unit; + } else { + // If not, we only display the first value + const oneFeature = data[0]; const { values } = oneFeature; - if (values.length === 0) { - return; + if (values.length > 0) { + const firstElement = values[0]; + const lastElement = values[values.length - 1]; + newState.variation = calculateVariation(firstElement.value, lastElement.value); + newState.lastValueRounded = roundWith2DecimalIfNeeded(lastElement.value); + newState.unit = unit; } - const firstElement = values[0]; - const lastElement = values[values.length - 1]; - const variation = calculateVariation(firstElement.value, lastElement.value); - const lastValue = lastElement.value; - variationArray.push(variation); - lastValuesArray.push(lastValue); - }); - newState.variation = average(variationArray); - newState.lastValueRounded = roundWith2DecimalIfNeeded(average(lastValuesArray)); - newState.unit = unit; - } else if (data.length > 0) { - // If not, we only display the first value - const oneFeature = data[0]; - const { values } = oneFeature; - if (values.length > 0) { - const firstElement = values[0]; - const lastElement = values[values.length - 1]; - newState.variation = calculateVariation(firstElement.value, lastElement.value); - newState.lastValueRounded = roundWith2DecimalIfNeeded(lastElement.value); - newState.unit = unit; } }