dashboard.html 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. {% extends "base.html" %}
  2. {% set active_page = "dashboard" %}
  3. {% block title %} Dashboard {% endblock %}
  4. {% block divs %}
  5. <style>
  6. .leaflet-marker-popup a {
  7. text-decoration: none;
  8. color: white;
  9. }
  10. </style>
  11. <div class="container-fluid">
  12. <div class="row">
  13. <div class="col-sm-12">
  14. <div id="mapid"></div>
  15. <table class="table" style="font-size: 100%;">
  16. <thead>
  17. <tr>
  18. <th></th>
  19. {% for asset_group_name in asset_groups if asset_groups[asset_group_name].count > 0 %}
  20. {# On demo, show all non-empty groups, otherwise show all groups that are non-empty for the current user #}
  21. <th {% if asset_groups[asset_group_name].hover_label %}
  22. title="{{ asset_groups[asset_group_name].hover_label | capitalize }}" {% endif %}
  23. class="text-center{% if asset_group_name in aggregate_type_groups %} agg-group{% endif %}">
  24. {{ asset_group_name | capitalize }}
  25. </th>
  26. {% endfor %}
  27. </tr>
  28. </thead>
  29. <tbody>
  30. {% if not group_by_accounts %}
  31. <!-- Asset type icons -->
  32. <tr>
  33. <td></td>
  34. {% for asset_group_name in asset_groups if asset_groups[asset_group_name].count > 0 %}
  35. <td class="text-center{% if asset_group_name in aggregate_type_groups %} agg-group{% endif %}">
  36. <div style="min-height: 20px; padding:15px;">
  37. <i class="{{ asset_group_name | asset_icon }} center-icon"></i>
  38. </div>
  39. </td>
  40. {% endfor %}
  41. </tr>
  42. {% endif %}
  43. <tr>
  44. <td>
  45. {% if user_has_admin_reader_rights %}
  46. {{ FLEXMEASURES_PLATFORM_NAME }} total:
  47. {% else %}
  48. My assets:
  49. {% endif %}
  50. </td>
  51. {% for asset_group_name in asset_groups if asset_groups[asset_group_name].count > 0 %}
  52. <td class="text-center{% if asset_group_name in aggregate_type_groups %} agg-group{% endif %}">
  53. {{ asset_groups[asset_group_name].count }}</td>
  54. {% endfor %}
  55. </tr>
  56. </tbody>
  57. </table>
  58. {% if user_has_admin_reader_rights %}
  59. <div class="row">
  60. <form class="col-sm-12 text-right form-inline" role="form" target="" method="get" id="dashboard-grouping-options">
  61. Group by account:
  62. <label class="switch">
  63. <input name="group_by_accounts" {% if group_by_accounts %} checked{% endif %} type="checkbox" data-toggle="toggle">
  64. <span class="slider"></span>
  65. </label>
  66. </form>
  67. </div>
  68. <script type="text/javascript">
  69. $(document).on('change', '#dashboard-grouping-options input[name="group_by_accounts"]', function () {
  70. $(this).closest('form').submit();
  71. })
  72. </script>
  73. {% endif %}
  74. </div>
  75. </div>
  76. </div>
  77. <!-- Initialise the map -->
  78. <script src="https://cdn.jsdelivr.net/npm/leaflet@{{ js_versions.leaflet }}/dist/leaflet-src.min.js"></script>
  79. <script src="{{ url_for('flexmeasures_ui.static', filename='js/map-init.js') }}?v={{ flexmeasures_version }}"></script>
  80. <script src="https://unpkg.com/leaflet.markercluster@{{ js_versions.leafletmarkercluster }}/dist/leaflet.markercluster.js"></script>
  81. <script src="https://unpkg.com/leaflet.markercluster.layersupport@{{ js_versions.leafletmarkerclusterlayersupport }}/dist/leaflet.markercluster.layersupport.js"></script>
  82. <script type="text/javascript">
  83. // Make an icon for each known asset type
  84. {% for asset_type_name in known_asset_types %}
  85. var {{ asset_type_name | parameterize | pluralize }}_icon = new L.DivIcon({
  86. className: 'map-icon',
  87. html: '<i class="icon-empty-marker center-icon supersize"></i><i class="overlay center-icon {{ asset_type_name | asset_icon }}"></i>',
  88. iconSize: [100, 100], // size of the icon
  89. iconAnchor: [50, 50], // point of the icon which will correspond to marker's location
  90. popupAnchor: [0, -50] // point from which the popup should open relative to the iconAnchor
  91. });
  92. /*
  93. var {{ asset_type_name | parameterize | pluralize }}_opportunity_icon = new L.DivIcon({
  94. className: 'map-icon opportunity',
  95. html: '<i class="{{ asset_type_name | asset_icon }}"></i>',
  96. iconSize: [24, 24], // size of the icon
  97. iconAnchor: [12, 12], // point of the icon which will correspond to marker's location
  98. popupAnchor: [0, -12] // point from which the popup should open relative to the iconAnchor
  99. });
  100. */
  101. {% endfor %}
  102. // create markers, keep them in separate lists (by asset type) to be put into layers
  103. {% for asset_group_name in asset_groups %}
  104. var {{ asset_groups[asset_group_name].parameterized_name }}_markers = [];
  105. {% for asset in asset_groups[asset_group_name].assets if asset.location %}
  106. if (typeof marker_for_{{ (asset.id) }} == 'undefined') {
  107. var marker_for_{{ (asset.id)
  108. }} = L
  109. .marker(
  110. [{{ asset.location[0] }}, {{ asset.location[1] }}],
  111. { icon: {{ asset.generic_asset_type.name | parameterize | pluralize }}_icon, id: {{ asset.id }}, parentId: {% if asset.parent_asset_id is none %}null{% else %}{{ asset.parent_asset_id }}{% endif %} }
  112. )
  113. .bindPopup(`
  114. <div class="leaflet-marker-popup">
  115. <div class="row">
  116. <div class="col-sm-9">
  117. <h4>{{ asset.name }} </h4>
  118. {% if user_has_admin_reader_rights %} <small>Account: {{ asset.owner.name }} </small> {% endif %}
  119. </div>
  120. </div>
  121. <div class="row top-buffer">
  122. <div class="col-sm-12">
  123. <!-- Add d-flex to make buttons appear in a row horizontally -->
  124. <div class="d-flex justify-content-start">
  125. <button class="btn btn-sm" style="margin-right: 5px;">
  126. <a href="assets/{{ asset.id }}">Context</a>
  127. </button>
  128. <button class="btn btn-sm" style="margin-right: 5px;">
  129. <a href="assets/{{ asset.id }}/graphs">Graphs</a>
  130. </button>
  131. <button class="btn btn-sm" style="margin-right: 5px;">
  132. <a href="assets/{{ asset.id }}/properties">Properties</a>
  133. </button>
  134. <button class="btn btn-sm" style="margin-right: 5px;">
  135. <a href="assets/{{ asset.id }}/status">Status</a>
  136. </button>
  137. </div>
  138. </div>
  139. </div>
  140. </div>
  141. `
  142. )
  143. .on('mouseover', function () {
  144. $(this._icon).addClass('over');
  145. })
  146. .on('mouseout', function () {
  147. $(this._icon).delay(3000).queue(function (next) {
  148. $(this).removeClass('over');
  149. next();
  150. });
  151. })
  152. .bindTooltip({% if user_has_admin_reader_rights %}"{{ asset.name }} ({{ asset.owner.name }})"{% else %}"{{ asset.name }}" {% endif %},
  153. {
  154. permanent: false,
  155. direction: 'right'
  156. })
  157. .on('click', clickPan)
  158. // .openPopup();
  159. {{ asset_groups[asset_group_name].parameterized_name }}_markers.push(marker_for_{{ (asset.id) }});
  160. }
  161. {% endfor %}
  162. {% endfor %}
  163. // Monkeypatch spiderfy
  164. const originalSpiderfy = L.MarkerCluster.prototype.spiderfy;
  165. L.MarkerCluster.include({
  166. spiderfy: function () {
  167. // Send spiderfyShapePositions the markers themselves, rather than just the number of markers
  168. passMarkersToSpiderfyShapePositions(this)
  169. return originalSpiderfy.call(this);
  170. }
  171. });
  172. // Monkeypatch _animationSpiderfy
  173. const originalAnimationSpiderfy = L.MarkerCluster.prototype._animationSpiderfy;
  174. L.MarkerCluster.include({
  175. _animationSpiderfy: _animationTreeSpiderfy,
  176. });
  177. // Monkeypatch unspiderfy
  178. const originalUnspiderfy = L.MarkerCluster.prototype.unspiderfy;
  179. L.MarkerCluster.include({
  180. unspiderfy: function () {
  181. // Remove spider legs straight away (the default animation does not suit a tree view)
  182. removeSpiderLegs(this);
  183. return originalUnspiderfy.call(this);
  184. }
  185. });
  186. // create Map with tiles
  187. var assetMap = L
  188. .map('mapid', { center: [{{ map_center[0] }}, {{ map_center[1] }}], zoom: 6 })
  189. .on('popupopen', function () {
  190. $(function () {
  191. $('[data-toggle="tooltip"]').tooltip();
  192. });
  193. });
  194. addTileLayer(assetMap, '{{ mapboxAccessToken }}');
  195. var mcgLayerSupportGroup = L.markerClusterGroup.layerSupport({
  196. spiderfyShapePositionsWithMarkers: computeCenteredTreeLayout
  197. });
  198. var control = L.control.layers(null, null, { collapsed: true });
  199. // add a layer for each asset type
  200. {% for asset_group_name in asset_groups %}
  201. {% if asset_groups[asset_group_name].count > 0 %}
  202. var {{ asset_groups[asset_group_name].parameterized_name }}_layer = new L.LayerGroup({{ asset_groups[asset_group_name].parameterized_name }}_markers);
  203. mcgLayerSupportGroup.checkIn({{ asset_groups[asset_group_name].parameterized_name }}_layer);
  204. control.addOverlay({{ asset_groups[asset_group_name].parameterized_name }}_layer, "{{ asset_group_name | capitalize }}")
  205. {{ asset_groups[asset_group_name].parameterized_name }}_layer.addTo(assetMap);
  206. {% endif %}
  207. {% endfor %}
  208. mcgLayerSupportGroup.addTo(assetMap);
  209. control.addTo(assetMap);
  210. </script>
  211. {% endblock %}