Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/ui/breadcrumb' into feat…
Browse files Browse the repository at this point in the history
…ure/ui/breadcrumb
  • Loading branch information
victorgarcia98 committed Dec 15, 2023
2 parents 1a7427d + 3fba955 commit fe5d79d
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 37 deletions.
4 changes: 4 additions & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ v0.18.0 | December XX, 2023
New features
-------------

* Better navigation experience through listings (sensors / assets / users / accounts) in the :abbr:`UI (user interface)`, by heading to the selected entity upon a click (or CTRL + click) anywhere within a row [see `PR #923 <https://github.com/FlexMeasures/flexmeasures/pull/923>`_]

Infrastructure / Support
----------------------

* New documentation section on constructing a flex model for :abbr:`V2G (vehicle-to-grid)` [see `PR #885 <https://github.com/FlexMeasures/flexmeasures/pull/885>`_]
* Allow charts in plugins to show currency codes (such as EUR) as currency symbols (€) [see `PR #922 <https://github.com/FlexMeasures/flexmeasures/pull/922>`_]

Bugfixes
-----------

* Give `admin-reader` role access to the RQ Scheduler dashboard [see `PR #901 <https://github.com/FlexMeasures/flexmeasures/pull/901>`_]
* Assets without a geographical position (i.e. no lat/lng coordinates) can be edited through the UI [see `PR #924 <https://github.com/FlexMeasures/flexmeasures/pull/924>`_]


v0.17.1 | December 7, 2023
Expand Down
10 changes: 7 additions & 3 deletions flexmeasures/ui/crud/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from flask_wtf import FlaskForm
from flask_security import login_required, current_user
from wtforms import StringField, DecimalField, SelectField
from wtforms.validators import DataRequired
from wtforms.validators import DataRequired, optional
from flexmeasures.auth.policy import user_has_admin_access

from flexmeasures.data import db
Expand Down Expand Up @@ -40,11 +40,13 @@ class AssetForm(FlaskForm):
name = StringField("Name")
latitude = DecimalField(
"Latitude",
validators=[optional()],
places=None,
render_kw={"placeholder": "--Click the map or enter a latitude--"},
)
longitude = DecimalField(
"Longitude",
validators=[optional()],
places=None,
render_kw={"placeholder": "--Click the map or enter a longitude--"},
)
Expand All @@ -65,8 +67,10 @@ def validate_on_submit(self):
def to_json(self) -> dict:
"""turn form data into a JSON we can POST to our internal API"""
data = copy.copy(self.data)
data["longitude"] = float(data["longitude"])
data["latitude"] = float(data["latitude"])
if data.get("longitude") is not None:
data["longitude"] = float(data["longitude"])
if data.get("latitude") is not None:
data["latitude"] = float(data["latitude"])

if "csrf_token" in data:
del data["csrf_token"]
Expand Down
9 changes: 9 additions & 0 deletions flexmeasures/ui/static/css/flexmeasures.css
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ i:not(.supersize):before, i:not(.supersize):after {
/* icon on the left (use left-icon class) */
:not(.map-icon) > i.left-icon {
left: 40px;
/* Prevent user agent stylesheets from treating this tag as an italics tag */
font-style: normal;
}
:not(.map-icon) > i.left-icon:after, :not(.map-icon) > i.left-icon:before {
position: absolute;
Expand Down Expand Up @@ -818,6 +820,13 @@ i.icon-wind:hover:before {

/* --- Tables ---- */

/* Clickable list items */

table.dataTable.nav-on-click tbody tr:hover {
background-color: var(--nav-current-hover-background-color) !important;
cursor: pointer;
}

/* scrolling */
.floatThead-wrapper table {
width: 100%;
Expand Down
100 changes: 97 additions & 3 deletions flexmeasures/ui/static/js/flexmeasures.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,50 @@ function defaultImage(action) {
}


function clickableTable(element, urlColumn) {
// This will keep actions like text selection or dragging functional
var table = $(element).DataTable();
var tbody = element.getElementsByTagName('tbody')[0];
var startX, startY;
var radiusLimit = 0; // how much the mouse is allowed to move during clicking

$(tbody).on({
mousedown: function (event) {
startX = event.pageX;
startY = event.pageY;
},
mouseup: function (event) {
var endX = event.pageX;
var endY = event.pageY;

var deltaX = endX - startX;
var deltaY = endY - startY;

var euclidean = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

if (euclidean <= radiusLimit) {
var columnIndex = table.column(':contains(' + urlColumn + ')').index();
var data = table.row(this).data();
var url = data[columnIndex];
handleClick(event, url);
}
}
}, 'tr');
}


function handleClick(event, url) {
// ignore clicks on <a href> or <button> elements
if (['a', 'button'].includes(event.target.tagName.toLowerCase())) {
return
} else if (event.ctrlKey) {
window.open(url, "_blank");
} else {
window.open(url, "_self");
}
}


function ready() {

console.log("ready...");
Expand All @@ -61,7 +105,6 @@ function ready() {
}
);


// Table pagination

$.extend(true, $.fn.dataTable.defaults, {
Expand Down Expand Up @@ -98,6 +141,11 @@ function ready() {
$('.paginate-5').dataTable().api().page.len(5).draw();
$('.paginate-10').dataTable().api().page.len(10).draw();

// Tables with the nav-on-click class

navTables = document.getElementsByClassName('nav-on-click');
Array.prototype.forEach.call(navTables, function(t) {clickableTable(t, 'URL')});


// Sliders

Expand Down Expand Up @@ -316,12 +364,28 @@ function submit_sensor_type() {
/* Quantities incl. units
* Usage:
* {
* 'format': [<d3-format>, <sensor unit>],
* 'format': [<d3-format>, <sensor unit>, <optional preference to show currency symbol instead of currency code>],
* 'formatType': 'quantityWithUnitFormat'
* }
* The use of currency symbols, such as the euro sign (€), should be reserved for use in graphics.
* See, for example, https://publications.europa.eu/code/en/en-370303.htm
* The rationale behind this is that they are often ambiguous.
* For example, both the Australian dollar (AUD) and the United States dollar (USD) map to the dollar sign ($).
*/
vega.expressionFunction('quantityWithUnitFormat', function(datum, params) {
return d3.format(params[0])(datum) + " " + params[1];
const formatDef = {
"decimal": ".",
"thousands": ",",
"grouping": [3],
};
const locale = d3.formatLocale(formatDef);
// The third element on param allows choosing to show the currency symbol (true) or the currency name (false)
if (params.length > 2 && params[2] === true){
return locale.format(params[0])(datum) + " " + convertCurrencyCodeToSymbol(params[1]);
}
else {
return d3.format(params[0])(datum) + " " + params[1];
}
});

/*
Expand Down Expand Up @@ -359,3 +423,33 @@ vega.expressionFunction('timezoneFormat', function(date, params) {
const tzDate = new Date(0,0,0,0,Math.abs(tzOffsetNumber));
return `${ tzOffsetNumber > 0 ? '-' : '+'}${("" + tzDate.getHours()).padStart(2, '0')}:${("" + tzDate.getMinutes()).padStart(2, '0')}` + ' (' + timezoneString + ')';
});

/*
* Convert any currency codes in the unit to currency symbols.
* This relies on the currencyToSymbolMap imported from currency-symbol-map/map.js
*/
const convertCurrencyCodeToSymbol = (unit) => {
return replaceMultiple(unit, currencySymbolMap);
};

/**
* Replaces multiple substrings in a given string based on a provided mapping object.
*
* @param {string} str - The input string in which replacements will be performed.
* @param {Object} mapObj - An object where keys are substrings to be replaced, and values are their corresponding replacements.
* @returns {string} - A new string with the specified substitutions applied.
*
* @example
* // Replace currency codes with symbols in the given string
* const inputString = "The price is 50 EUR/MWh, and 30 AUD/MWh.";
* const currencyMapping = { EUR: '€', AUD: '$' };
* const result = replace_multiple(inputString, currencyMapping);
* // The result will be "The price is 50 €/MWh, and 30 $/MWh."
*/
function replaceMultiple(str, mapObj){
// Create a regular expression pattern using the keys of the mapObj joined with "|" (OR) to match any of the substrings.
let regex = new RegExp(Object.keys(mapObj).join("|"),"g");
// Use the regular expression to replace matched substrings with their corresponding values from the mapObj.
// The "g" flag makes the replacement global (replaces all occurrences), and it is case-sensitive by default.
return str.replace(regex, matched => mapObj[matched]);
}
4 changes: 4 additions & 0 deletions flexmeasures/ui/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,10 @@
<script src="https://cdn.jsdelivr.net/npm/vega@{{ js_versions.vega }}"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@{{ js_versions.vegalite }}"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@{{ js_versions.vegaembed }}"></script>
{# Workaround for loading a NodeJS module without NodeJS #}
<script>var module = { exports: {} };</script>
<script src="https://cdn.jsdelivr.net/npm/currency-symbol-map@{{ js_versions.currencysymbolmap }}/map.js"></script>
<script>const currencySymbolMap = module.exports;</script>
{% endif %}

<!-- Custom scripts -->
Expand Down
22 changes: 13 additions & 9 deletions flexmeasures/ui/templates/crud/account.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ <h3>Users</h3>
</label>
</div>
</form>
<table class="table table-striped table-responsive paginate">
<table class="table table-striped table-responsive paginate nav-on-click" title="View/edit this user">
<thead>
<tr>
<th>Username</th>
Expand All @@ -63,14 +63,14 @@ <h3>Users</h3>
<th>Last Login</th>
<th>Last Seen</th>
<th>Active</th>
<th class="hidden">URL</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>
<a href="/users/{{ user.id }}" title="View/edit this user">{{ user.username
}}</a>
{{ user.username }}
</td>
<td>
<a href="mailto:{{ user.email }}" title="Mail this user">{{ user.email }}</a>
Expand All @@ -91,7 +91,9 @@ <h3>Users</h3>
<td>
{{ user.active }}
</td>

<td class="hidden">
/users/{{ user.id }}
</td>
</tr>
{% endfor %}
</tbody>
Expand All @@ -100,10 +102,10 @@ <h3>Users</h3>
<div class="card">
<h3>Assets</h3>

<table class="table table-striped table-responsive paginate">
<table class="table table-striped table-responsive paginate nav-on-click" title="View this asset">
<thead>
<tr>
<th>Name</th>
<th><i class="left-icon">Name</i></th>
<th>Location</th>
<th>Asset ID</th>
<th>Account</th>
Expand All @@ -118,14 +120,14 @@ <h3>Assets</h3>
</form>
{% endif %}
</th>
<th class="hidden">URL</th>
</tr>
</thead>
<tbody>
{% for asset in assets %}
<tr>
<td>
<i class="{{ asset.generic_asset_type.name | asset_icon }} left-icon"><a
href="/assets/{{ asset.id }}" alt="View this asset">{{ asset.name }}</a></i>
<i class="{{ asset.generic_asset_type.name | asset_icon }} left-icon">{{ asset.name }}</i>
</td>
<td>
{% if asset.latitude and asset.longitude %}
Expand All @@ -148,7 +150,9 @@ <h3>Assets</h3>
</td>
<td class="text-right">
</td>

<td class="hidden">
/assets/{{ asset.id }}
</td>
</tr>
{% endfor %}
</tbody>
Expand Down
9 changes: 6 additions & 3 deletions flexmeasures/ui/templates/crud/accounts.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@
<div class="card">
<h3>All Accounts
</h3>
<table class="table table-striped table-responsive paginate">
<table class="table table-striped table-responsive paginate nav-on-click" title="View this account">
<thead>
<tr>
<th>Account</th>
<th>ID</th>
<th>Assets</th>
<th>Users</th>
<th>Roles</th>

<th class="hidden">URL</th>
</tr>
</thead>
<tbody>
{% for account in accounts %}
<tr>
<td>
<a href="/accounts/{{ account.id }}" title="View this account">{{ account.name }}</a>
{{ account.name }}
</td>
<td>
{{ account.id }}
Expand All @@ -37,6 +37,9 @@ <h3>All Accounts
<td>
{{ account.account_roles | map(attribute='name') | join(", ") }}
</td>
<td class="hidden">
/accounts/{{ account.id }}
</td>
</tr>
{% endfor %}
</tbody>
Expand Down
8 changes: 4 additions & 4 deletions flexmeasures/ui/templates/crud/asset.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,14 @@ <h3>Edit {{ asset.name }}</h3>
<div id="sensorchart" class="card" style="width: 100%;"></div>
<div class="sensors-asset card">
<h3>All sensors for {{ asset.name }}</h3>
<table class="table table-striped table-responsive paginate">
<table class="table table-striped table-responsive paginate nav-on-click" title="View data">
<thead>
<tr>
<th>Name</th>
<th class="text-right">Unit</th>
<th class="text-right">Resolution</th>
<th class="text-right no-sort">Entity address</th>
<th class="text-right no-sort">Data</th>
<th class="hidden">URL</th>
</tr>
</thead>
<tbody>
Expand All @@ -163,8 +163,8 @@ <h3>All sensors for {{ asset.name }}</h3>
<td class="text-right">
{{ sensor.entity_address }}
</td>
<td class="text-right">
<a href="/sensors/{{ sensor.id }}">View plot</a>
<td class="hidden">
/sensors/{{ sensor.id }}
</td>
</tr>
{% endfor %}
Expand Down
Loading

0 comments on commit fe5d79d

Please sign in to comment.