Merge branch 'ent-7273-Consola-Visual-nuevo-elemento-Gráfica-básica' into 'develop'

Ent 7273 consola visual nuevo elemento gráfica básica

See merge request artica/pandorafms!4304
This commit is contained in:
Daniel Rodriguez 2021-10-01 12:48:09 +00:00
commit 7904c61df5
22 changed files with 731 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

View File

@ -234,6 +234,7 @@ define('CLOCK', 19);
define('COLOR_CLOUD', 20);
define('NETWORK_LINK', 21);
define('ODOMETER', 22);
define('BASIC_CHART', 23);
// Some styles.
define('MIN_WIDTH', 300);
define('MIN_HEIGHT', 120);

View File

@ -835,6 +835,10 @@ function grafico_modulo_sparse($params)
$font_size = $config['font_size'];
}
if (isset($params['basic_chart']) === false) {
$params['basic_chart'] = false;
}
// If is metaconsole set 10pt size value.
if (is_metaconsole()) {
$font_size = '10';

View File

@ -1014,6 +1014,7 @@ function pandoraFlotArea(
var force_integer = 0;
var divisor = params.divisor;
var maximum_y_axis = params.maximum_y_axis;
var basic_chart = params.basic_chart;
if (typeof divisor === "undefined") {
divisor = 1000;
@ -2018,11 +2019,8 @@ function pandoraFlotArea(
grid: {
hoverable: true,
clickable: true,
borderWidth: 1,
borderColor: "#C1C1C1",
backgroundColor: background_color,
color: grid_color,
autoHighlight: true
color: grid_color
},
xaxis: {
min: min_x,
@ -2062,6 +2060,16 @@ function pandoraFlotArea(
}
};
if (basic_chart === true) {
options.grid.borderWidth = 0;
options.grid.backgroundColor = "rgba(255,255,255,0)";
options.grid.autoHighlight = false;
options.xaxis.show = false;
options.xaxis.tickLength = 0;
options.yaxis.show = false;
options.yaxis.tickLength = 0;
}
if (typeof maximum_y_axis !== "undefined" && maximum_y_axis != 0) {
options.yaxis.max = maximum_y_axis;
}

View File

@ -520,6 +520,9 @@ function createVisualConsole(
case "ODOMETER":
type = 22;
break;
case "BASIC_CHART":
type = 23;
break;
default:
type = 0;
}
@ -1240,6 +1243,9 @@ function createOrUpdateVisualConsoleItem(
case 22:
nameType = "Odometer";
break;
case 23:
nameType = "Basic chart";
break;
default:
nameType = "Static graph";

View File

@ -389,6 +389,9 @@ final class Container extends Model
case ODOMETER:
return Items\Odometer::class;
case BASIC_CHART:
return Items\BasicChart::class;
default:
return Item::class;
}

View File

@ -1792,6 +1792,7 @@ class Item extends CachedModel
'legendBackgroundColor',
'legendColor',
'titleColor',
'moduleNameColor',
]
),
null
@ -2119,6 +2120,10 @@ class Item extends CachedModel
$text = __('Odometer');
break;
case BASIC_CHART:
$text = __('Basic chart');
break;
default:
// Lines could not be parents.
continue 2;

View File

@ -0,0 +1,348 @@
<?php
declare(strict_types=1);
namespace Models\VisualConsole\Items;
use Models\VisualConsole\Item;
/**
* Model of a basic chart item of the Visual Console.
*/
final class BasicChart extends Item
{
/**
* Used to enable the fetching, validation and extraction of information
* about the linked module.
*
* @var boolean
*/
protected static $useLinkedModule = true;
/**
* Used to enable the fetching, validation and extraction of information
* about the linked visual console.
*
* @var boolean
*/
protected static $useLinkedVisualConsole = true;
/**
* Used to enable validation, extraction and encodeing of the HTML output.
*
* @var boolean
*/
protected static $useHtmlOutput = true;
/**
* Returns a valid representation of the model.
*
* @param array $data Input data.
*
* @return array Data structure representing the model.
*
* @overrides Item::decode.
*/
protected function decode(array $data): array
{
$return = parent::decode($data);
$return['type'] = BASIC_CHART;
$return['period'] = $this->extractPeriod($data);
$return['value'] = $this->extractValue($data);
$return['status'] = $this->extractStatus($data);
$return['moduleNameColor'] = $this->extractModuleNameColor($data);
return $return;
}
/**
* Extract a graph period value.
*
* @param array $data Unknown input data structure.
*
* @return mixed The time in seconds of the graph period or null.
*/
private static function extractPeriod(array $data)
{
return static::parseIntOr(
static::issetInArray($data, ['period']),
null
);
}
/**
* Extract value.
*
* @param array $data Unknown input data structure.
*
* @return mixed String representing value or null.
*/
private static function extractValue(array $data)
{
return static::notEmptyStringOr(
static::issetInArray(
$data,
['value']
),
'0'
);
}
/**
* Extract status value.
*
* @param array $data Unknown input data structure.
*
* @return mixed String representing status value or null.
*/
private static function extractStatus(array $data)
{
return static::notEmptyStringOr(
static::issetInArray(
$data,
['status']
),
COL_UNKNOWN
);
}
/**
* Extract label color value.
*
* @param array $data Unknown input data structure.
*
* @return mixed String representing the grid color (not empty) or null.
*/
private static function extractModuleNameColor(array $data): string
{
return static::notEmptyStringOr(
static::issetInArray($data, ['moduleNameColor', 'border_color']),
'#3f3f3f'
);
}
/**
* Fetch a vc item data structure from the database using a filter.
*
* @param array $filter Filter of the Visual Console Item.
*
* @return array The Visual Console Item data structure stored into the DB.
* @throws \InvalidArgumentException When an agent Id cannot be found.
*
* @override Item::fetchDataFromDB.
*/
protected static function fetchDataFromDB(
array $filter,
?float $ratio=0,
?float $widthRatio=0
): array {
// Due to this DB call, this function cannot be unit tested without
// a proper mock.
$data = parent::fetchDataFromDB($filter, $ratio, $widthRatio);
/*
* Retrieve extra data.
*/
// Load side libraries.
global $config;
include_once $config['homedir'].'/include/functions_graph.php';
include_once $config['homedir'].'/include/functions_modules.php';
if (is_metaconsole()) {
\enterprise_include_once('include/functions_metaconsole.php');
}
$imageOnly = false;
$period = static::extractPeriod($data);
$linkedModule = static::extractLinkedModule($data);
$moduleId = $linkedModule['moduleId'];
$metaconsoleId = $linkedModule['metaconsoleId'];
// Maybe connect to node.
$nodeConnected = false;
if (\is_metaconsole() === true && $metaconsoleId !== null) {
$nodeConnected = \metaconsole_connect(
null,
$metaconsoleId
) === NOERR;
if ($nodeConnected === false) {
throw new \InvalidArgumentException(
'error connecting to the node'
);
}
}
/*
* About the 30 substraction to the graph height:
* The function which generates the graph doesn't respect the
* required height. It uses it for the canvas (the graph itself and
* their axes), but then it adds the legend. One item of the legend
* (one dataset) is about 30px, so we need to substract that height
* from the canvas to try to fit the element's height.
*
* PD: The custom graphs can have more datasets, but we only substract
* the height of one of it to replicate the legacy functionality.
*/
$width = (int) $data['width'];
$height = ((int) $data['height'] * 0.6);
// Module graph.
if ($moduleId === null) {
throw new \InvalidArgumentException('missing module Id');
}
$now = new \DateTime();
$date_array = [];
$date_array['period'] = $period;
$date_array['final_date'] = $now->getTimestamp();
$date_array['start_date'] = ($now->getTimestamp() - $period);
$params = [
'agent_module_id' => $moduleId,
'period' => $period,
'show_events' => false,
'width' => $width,
'height' => $height,
'title' => \modules_get_agentmodule_name(
$moduleId
),
'unit' => \modules_get_unit($moduleId),
'only_image' => $imageOnly,
'menu' => false,
'vconsole' => true,
'return_img_base_64' => true,
'show_legend' => false,
'show_title' => false,
'dashboard' => true,
'backgroundColor' => 'transparent',
'server_id' => $metaconsoleId,
'basic_chart' => true,
];
if ($imageOnly !== false) {
$imgbase64 = 'data:image/jpg;base64,';
}
$imgbase64 .= \grafico_modulo_sparse($params);
$data['html'] = $imgbase64;
$data['value'] = \modules_get_last_value($moduleId);
$data['status'] = \modules_get_color_status(modules_get_agentmodule_last_status($moduleId));
// Restore connection.
if ($nodeConnected === true) {
\metaconsole_restore_db();
}
return $data;
}
/**
* Generates inputs for form (specific).
*
* @param array $values Default values.
*
* @return array Of inputs.
*
* @throws Exception On error.
*/
public static function getFormInputs(array $values): array
{
// Default values.
$values = static::getDefaultGeneralValues($values);
// Retrieve global - common inputs.
$inputs = Item::getFormInputs($values);
if (is_array($inputs) !== true) {
throw new \Exception(
'[BasicChart]::getFormInputs parent class return is not an array'
);
}
if ($values['tabSelected'] === 'specific') {
// Default values.
if (isset($values['period']) === false) {
$values['period'] = 3600;
}
// Autocomplete agents.
$inputs[] = [
'id' => 'BCautoCompleteAgent',
'label' => __('Agent'),
'arguments' => [
'type' => 'autocomplete_agent',
'name' => 'agentAlias',
'id_agent_hidden' => $values['agentId'],
'name_agent_hidden' => 'agentId',
'server_id_hidden' => $values['metaconsoleId'],
'name_server_hidden' => 'metaconsoleId',
'return' => true,
'module_input' => true,
'module_name' => 'moduleId',
'module_none' => false,
],
];
// Autocomplete module.
$inputs[] = [
'id' => 'BCautoCompleteModule',
'label' => __('Module'),
'arguments' => [
'type' => 'autocomplete_module',
'name' => 'moduleId',
'selected' => $values['moduleId'],
'return' => true,
'sort' => false,
'agent_id' => $values['agentId'],
'metaconsole_id' => $values['metaconsoleId'],
],
];
// Period.
$inputs[] = [
'label' => __('Period'),
'arguments' => [
'name' => 'period',
'type' => 'interval',
'value' => $values['period'],
'nothing' => __('None'),
'nothing_value' => 0,
],
];
// module name color.
$inputs[] = [
'label' => __('Module name color'),
'arguments' => [
'wrapper' => 'div',
'name' => 'moduleNameColor',
'type' => 'color',
'value' => $values['moduleNameColor'],
'return' => true,
],
];
// Inputs LinkedVisualConsole.
$inputsLinkedVisualConsole = self::inputsLinkedVisualConsole(
$values
);
foreach ($inputsLinkedVisualConsole as $key => $value) {
$inputs[] = $value;
}
}
return $inputs;
}
}

View File

@ -528,6 +528,19 @@ class View extends \HTML
}
break;
case BASIC_CHART:
$data['agentId'] = \get_parameter('agentId');
$data['metaconsoleId'] = \get_parameter('metaconsoleId');
$data['agentAlias'] = \get_parameter('agentAlias');
$data['moduleId'] = \get_parameter('moduleId');
$data['period'] = \get_parameter('period');
$data['moduleNameColor'] = \get_parameter('moduleNameColor');
if ($itemId === 0) {
$data['height'] = 110;
$data['width'] = 375;
}
break;
default:
// Not posible.
break;

View File

@ -426,6 +426,19 @@ input.odometer_min_white[disabled] {
background: url(../../images/odometer.png) no-repeat center;
}
input.basic_chart_min {
background: url(../../images/basic_chart.png) no-repeat center;
}
input.basic_chart_min[disabled] {
background: url(../../images/basic_chart.disabled.png) no-repeat center;
}
input.basic_chart_min_white {
background: url(../../images/basic_chart.disabled.png) no-repeat center;
}
input.basic_chart_min_white[disabled] {
background: url(../../images/basic_chart.png) no-repeat center;
}
div#cont {
position: fixed;
max-height: 320px;

View File

@ -547,6 +547,44 @@ div.module-graph {
justify-content: center;
}
div.basic-chart {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-end;
justify-content: center;
}
.basic-chart-header {
height: 40%;
width: 100%;
display: flex;
}
.basic-chart-header-name {
margin: 0;
padding: 0;
height: 100%;
width: 80%;
display: flex;
align-items: center;
font-size: 2.5vmin;
margin-left: 3%;
}
.basic-chart-header-value {
margin: 0;
padding: 0;
height: 100%;
width: 20%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5vmin;
}
div.module-graph .gauge_d3_class {
flex: 1 1 100px;
float: none !important;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -266,6 +266,7 @@ if ($pure === false) {
$class_cloud = 'color_cloud_min link-create-item';
$class_nlink = 'network_link_min link-create-item';
$class_odometer = 'odometer_min link-create-item';
$class_basic_chart = 'basic_chart_min link-create-item';
$class_delete = 'delete_item delete_min';
$class_copy = 'copy_item';
if ($config['style'] === 'pandora_black') {
@ -285,6 +286,7 @@ if ($pure === false) {
$class_cloud = 'color_cloud_min_white link-create-item';
$class_nlink = 'network_link_min_white link-create-item';
$class_odometer = 'odometer_min_white link-create-item';
$class_basic_chart = 'basic_chart_min_white link-create-item';
$class_delete = 'delete_item_white delete_min_white';
$class_copy = 'copy_item_white';
}
@ -304,6 +306,11 @@ if ($pure === false) {
__('Module Graph'),
$class_module_graph
);
visual_map_print_button_editor_refactor(
'BASIC_CHART',
__('Basic chart'),
$class_basic_chart
);
visual_map_print_button_editor_refactor(
'DONUT_GRAPH',
__('Serialized pie graph'),

View File

@ -119,6 +119,7 @@ return array(
'Models\\VisualConsole\\Items\\SimpleValue' => $baseDir . '/include/rest-api/models/VisualConsole/Items/SimpleValue.php',
'Models\\VisualConsole\\Items\\StaticGraph' => $baseDir . '/include/rest-api/models/VisualConsole/Items/StaticGraph.php',
'Models\\VisualConsole\\Items\\Odometer' => $baseDir . '/include/rest-api/models/VisualConsole/Items/Odometer.php',
'Models\\VisualConsole\\Items\\BasicChart' => $baseDir . '/include/rest-api/models/VisualConsole/Items/BasicChart.php',
'Models\\VisualConsole\\View' => $baseDir . '/include/rest-api/models/VisualConsole/View.php',
'Mpdf\\Barcode' => $vendorDir . '/mpdf/mpdf/src/Barcode.php',
'Mpdf\\Barcode\\AbstractBarcode' => $vendorDir . '/mpdf/mpdf/src/Barcode/AbstractBarcode.php',

View File

@ -193,6 +193,7 @@ class ComposerStaticInitfdecadadce22e6dde51e9535fe4ad7aa
'Models\\VisualConsole\\Items\\SimpleValue' => __DIR__ . '/../..' . '/include/rest-api/models/VisualConsole/Items/SimpleValue.php',
'Models\\VisualConsole\\Items\\StaticGraph' => __DIR__ . '/../..' . '/include/rest-api/models/VisualConsole/Items/StaticGraph.php',
'Models\\VisualConsole\\Items\\Odometer' => __DIR__ . '/../..' . '/include/rest-api/models/VisualConsole/Items/Odometer.php',
'Models\\VisualConsole\\Items\\BasicChart' => __DIR__ . '/../..' . '/include/rest-api/models/VisualConsole/Items/BasicChart.php',
'Models\\VisualConsole\\View' => __DIR__ . '/../..' . '/include/rest-api/models/VisualConsole/View.php',
'Mpdf\\Barcode' => __DIR__ . '/..' . '/mpdf/mpdf/src/Barcode.php',
'Mpdf\\Barcode\\AbstractBarcode' => __DIR__ . '/..' . '/mpdf/mpdf/src/Barcode/AbstractBarcode.php',

View File

@ -48,7 +48,8 @@ export const enum ItemType {
CLOCK = 19,
COLOR_CLOUD = 20,
NETWORK_LINK = 21,
ODOMETER = 22
ODOMETER = 22,
BASIC_CHART = 23
}
// Base item properties. This interface should be extended by the item implementations.
@ -220,6 +221,9 @@ export function titleItem(id: number): string {
case ItemType.ODOMETER:
title = t("Odometer");
break;
case ItemType.BASIC_CHART:
title = t("Basic chart");
break;
default:
title = t("Item");
break;

View File

@ -38,6 +38,7 @@ import BarsGraph, { barsGraphPropsDecoder } from "./items/BarsGraph";
import ModuleGraph, { moduleGraphPropsDecoder } from "./items/ModuleGraph";
import Service, { servicePropsDecoder } from "./items/Service";
import Odometer, { odometerPropsDecoder } from "./items/Odometer";
import BasicChart, { basicChartPropsDecoder } from "./items/BasicChart";
// TODO: Document.
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
@ -88,6 +89,8 @@ function itemInstanceFrom(data: AnyObject) {
return new NetworkLink(networkLinkPropsDecoder(data), meta);
case ItemType.ODOMETER:
return new Odometer(odometerPropsDecoder(data), meta);
case ItemType.BASIC_CHART:
return new BasicChart(basicChartPropsDecoder(data), meta);
default:
throw new TypeError("item not found");
}
@ -140,6 +143,8 @@ function decodeProps(data: AnyObject) {
return networkLinkPropsDecoder(data);
case ItemType.ODOMETER:
return odometerPropsDecoder(data);
case ItemType.BASIC_CHART:
return basicChartPropsDecoder(data);
default:
throw new TypeError("decoder not found");
}
@ -1287,7 +1292,8 @@ export default class VisualConsole {
[ItemType.CLOCK]: Clock,
[ItemType.COLOR_CLOUD]: ColorCloud,
[ItemType.NETWORK_LINK]: NetworkLink,
[ItemType.ODOMETER]: Odometer
[ItemType.ODOMETER]: Odometer,
[ItemType.BASIC_CHART]: BasicChart
};
/**
@ -1343,6 +1349,9 @@ export default class VisualConsole {
case ItemType.ODOMETER:
text = t("Odometer");
break;
case ItemType.BASIC_CHART:
text = t("BasicChart");
break;
default:
text = t("Item");
break;

View File

@ -0,0 +1,222 @@
import {
LinkedVisualConsoleProps,
AnyObject,
WithModuleProps
} from "../lib/types";
import {
linkedVCPropsDecoder,
modulePropsDecoder,
decodeBase64,
stringIsEmpty,
parseIntOr
} from "../lib";
import Item, { ItemType, ItemProps, itemBasePropsDecoder } from "../Item";
export type BasicChartProps = {
type: ItemType.BASIC_CHART;
html: string;
period: number | null;
value: number | null;
status: string;
moduleNameColor: string;
} & ItemProps &
WithModuleProps &
LinkedVisualConsoleProps;
/**
* Build a valid typed object from a raw object.
* This will allow us to ensure the type safety.
*
* @param data Raw object.
* @return An object representing the basic chart props.
* @throws Will throw a TypeError if some property
* is missing from the raw object or have an invalid type.
*/
export function basicChartPropsDecoder(
data: AnyObject
): BasicChartProps | never {
if (stringIsEmpty(data.html) && stringIsEmpty(data.encodedHtml)) {
throw new TypeError("missing html content.");
}
return {
...itemBasePropsDecoder(data), // Object spread. It will merge the properties of the two objects.
type: ItemType.BASIC_CHART,
html: !stringIsEmpty(data.html)
? data.html
: decodeBase64(data.encodedHtml),
period: parseIntOr(data.period, null),
value: parseFloat(data.value),
status: stringIsEmpty(data.status) ? "#B2B2B2" : data.status,
moduleNameColor: stringIsEmpty(data.moduleNameColor)
? "#3f3f3f"
: data.moduleNameColor,
...modulePropsDecoder(data), // Object spread. It will merge the properties of the two objects.
...linkedVCPropsDecoder(data) // Object spread. It will merge the properties of the two objects.
};
}
export default class BasicChart extends Item<BasicChartProps> {
protected createDomElement(): HTMLElement {
const element = document.createElement("div");
const header = document.createElement("div");
header.className = "basic-chart-header";
const moduleName = document.createElement("h2");
moduleName.className = "basic-chart-header-name";
moduleName.textContent = this.props.moduleName;
moduleName.style.color = `${this.props.moduleNameColor}`;
header.appendChild(moduleName);
let value = "";
if (this.props.value !== null) {
value = this.number_format(this.props.value, false, "", 2, 1000);
}
const moduleValue = document.createElement("h2");
moduleValue.className = "basic-chart-header-value";
moduleValue.textContent = `${value}`;
moduleValue.style.color = this.props.status;
header.appendChild(moduleValue);
element.innerHTML = this.props.html;
element.className = "basic-chart";
if (
this.props.agentDisabled === true ||
this.props.moduleDisabled === true
) {
element.style.opacity = "0.2";
}
// Remove the overview graph.
const legendP = element.getElementsByTagName("p");
for (let i = 0; i < legendP.length; i++) {
legendP[i].style.margin = "0px";
}
// Remove the overview graph.
const overviewGraphs = element.getElementsByClassName("overview_graph");
for (let i = 0; i < overviewGraphs.length; i++) {
overviewGraphs[i].remove();
}
// Hack to execute the JS after the HTML is added to the DOM.
const scripts = element.getElementsByTagName("script");
for (let i = 0; i < scripts.length; i++) {
if (scripts[i].src.length === 0) {
setTimeout(() => {
try {
eval(scripts[i].innerHTML.trim());
} catch (ignored) {} // eslint-disable-line no-empty
}, 0);
}
}
element.innerHTML = this.props.html;
element.insertBefore(header, element.firstChild);
return element;
}
protected updateDomElement(element: HTMLElement): void {
const header = document.createElement("div");
header.className = "basic-chart-header";
const moduleName = document.createElement("h2");
moduleName.className = "basic-chart-header-name";
moduleName.textContent = this.props.moduleName;
moduleName.style.color = `${this.props.moduleNameColor}`;
header.appendChild(moduleName);
let value = "";
if (this.props.value !== null) {
value = this.number_format(this.props.value, false, "", 2, 1000);
}
const moduleValue = document.createElement("h2");
moduleValue.className = "basic-chart-header-value";
moduleValue.textContent = `${value}`;
moduleValue.style.color = this.props.status;
header.appendChild(moduleValue);
element.innerHTML = this.props.html;
element.insertBefore(header, element.firstChild);
// Remove the overview graph.
const legendP = element.getElementsByTagName("p");
for (let i = 0; i < legendP.length; i++) {
legendP[i].style.margin = "0px";
}
// Remove the overview graph.
const overviewGraphs = element.getElementsByClassName("overview_graph");
for (let i = 0; i < overviewGraphs.length; i++) {
overviewGraphs[i].remove();
}
// Hack to execute the JS after the HTML is added to the DOM.
const scripts = element.getElementsByTagName("script");
for (let i = 0; i < scripts.length; i++) {
if (scripts[i].src.length === 0) {
eval(scripts[i].innerHTML.trim());
}
}
}
protected number_format(
number: number,
force_integer: boolean,
unit: string,
short_data: number,
divisor: number
) {
divisor = typeof divisor !== "undefined" ? divisor : 1000;
var decimals = 2;
// Set maximum decimal precision to 99 in case short_data is not set.
if (!short_data) {
short_data = 99;
}
if (force_integer) {
if (Math.round(number) != number) {
return "";
}
} else {
short_data++;
const aux_decimals = this.pad("1", short_data, 0);
number =
Math.round(number * Number.parseInt(aux_decimals)) /
Number.parseInt(aux_decimals);
}
var shorts = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"];
var pos = 0;
while (Math.abs(number) >= divisor) {
// As long as the number can be divided by 1000 or 1024.
pos++;
number = number / divisor;
}
if (divisor) {
number = Math.round(number * decimals) / decimals;
} else {
number = Math.round(number * decimals);
}
if (isNaN(number)) {
number = 0;
}
return number + " " + shorts[pos] + unit;
}
protected pad(input: string, length: number, padding: number): string {
var str = input + "";
return length <= str.length
? str
: this.pad(str + padding, length, padding);
}
}

View File

@ -547,6 +547,44 @@ div.module-graph {
justify-content: center;
}
div.basic-chart {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-end;
justify-content: center;
}
.basic-chart-header {
height: 40%;
width: 100%;
display: flex;
}
.basic-chart-header-name {
margin: 0;
padding: 0;
height: 100%;
width: 80%;
display: flex;
align-items: center;
font-size: 2.5vmin;
margin-left: 3%;
}
.basic-chart-header-value {
margin: 0;
padding: 0;
height: 100%;
width: 20%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5vmin;
}
div.module-graph .gauge_d3_class {
flex: 1 1 100px;
float: none !important;