Merge "Hacking on AJAX"
authorSawyer Bergeron <sbergeron@iol.unh.edu>
Mon, 8 Jul 2019 14:36:23 +0000 (14:36 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Mon, 8 Jul 2019 14:36:23 +0000 (14:36 +0000)
Makefile
src/static/css/base.css
src/static/js/dashboard.js
src/templates/booking/quick_deploy.html
src/templates/dashboard/multiple_select_filter_widget.html
src/templates/dashboard/searchable_select_multiple.html
src/templates/resource/steps/define_hardware.html

index 31243b0..cc7f70a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,5 @@
+.PHONY: build dev-up dev-start dev-stop up start stop data shell-nginx shell-web shell-db log-nginx log-web log-ps log-rmq log-worker
+
 build:
        docker-compose -f docker-compose.yml -f docker-compose.override-dev.yml build
 
index 1494e77..9fec97e 100644 (file)
@@ -66,9 +66,6 @@ a[aria-expanded="true"] > i.rotate {
     box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(109, 243, 76, 0.6);
     transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s;
 }
-#grid_wrapper > .row > div:first-child {
-    border-right: 1px solid gray;
-}
 
 /* Cursor effects */
 .not-allowed {
@@ -81,14 +78,18 @@ a[aria-expanded="true"] > i.rotate {
     top: 0;
 }
 
-/* Dropdown for collaborators */
-#drop_results {
-    max-height: 10rem;
+.z-2 {
     z-index: 2;
 }
 
-#drop_results > li {
-    word-wrap: anywhere;
+.mh-30vh {
+    max-height: 30vh;
+}
+
+.overflow-ellipsis {
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
 }
 
 /* Graphing for networks */
index 7f8a427..b74ead9 100644 (file)
@@ -1236,12 +1236,13 @@ class SearchableSelectMultipleWidget {
 
         for( const id in ids )
         {
-            const result_entry = document.createElement("li");
-            const result_button = document.createElement("a");
             const obj = this.items[id];
             const result_text = this.generate_element_text(obj);
-            result_entry.classList.add("list-group-item", "list-group-item-action");
+            const result_entry = document.createElement("a");
+            result_entry.href = "#";
             result_entry.innerText = result_text;
+            result_entry.title = result_text;
+            result_entry.classList.add("list-group-item", "list-group-item-action", "overflow-ellipsis", "flex-shrink-0");
             result_entry.onclick = function() { searchable_select_multiple_widget.select_item(obj.id); };
             const tooltip = document.createElement("span");
             const tooltiptext = document.createTextNode(result_text);
@@ -1300,21 +1301,39 @@ class SearchableSelectMultipleWidget {
             added_list.removeChild(added_list.firstChild);
         }
 
-        let list_html = "";
+        const list_html = document.createElement("div");
+        list_html.classList.add("list-group");
 
         for( const item_id of this.added_items )
         {
-            const item = this.items[item_id];
+            const times = document.createElement("li");
+            times.classList.add("fas", "fa-times");
+
+            const deleteButton = document.createElement("a");
+            deleteButton.href = "#";
+            deleteButton.innerHTML = "<i class='fas fa-times'></i>"
+            // Setting .onclick/.addEventListener does not work,
+            // which is why I took the setAttribute approach
+            // If anyone knows why, please let me know :]
+            deleteButton.setAttribute("onclick", `searchable_select_multiple_widget.remove_item(${item_id});`);
+            deleteButton.classList.add("btn");
+            const deleteColumn = document.createElement("div");
+            deleteColumn.classList.add("col-auto");
+            deleteColumn.append(deleteButton);
 
+            const item = this.items[item_id];
             const element_entry_text = this.generate_element_text(item);
+            const textColumn = document.createElement("div");
+            textColumn.classList.add("col", "overflow-ellipsis");
+            textColumn.innerText = element_entry_text;
+            textColumn.title = element_entry_text;
+
+            const itemRow = document.createElement("div");
+            itemRow.classList.add("list-group-item", "d-flex", "p-0", "align-items-center");
+            itemRow.append(textColumn, deleteColumn);
 
-            list_html += '<div class="border rounded mt-2 w-100 d-flex align-items-center pl-2">'
-                + element_entry_text
-                + '<button onclick="searchable_select_multiple_widget.remove_item('
-                + item_id
-                + ')" class="btn btn-danger ml-auto">Remove</button>';
-            list_html += '</div>';
+            list_html.append(itemRow);
         }
-        added_list.innerHTML = list_html;
+        added_list.innerHTML = list_html.innerHTML;
     }
 }
index 50ec59a..6776980 100644 (file)
@@ -7,13 +7,13 @@
 <form id="quick_booking_form" action="/booking/quick/" method="POST" class="form">
     {% csrf_token %}
     <div class="container-fluid">
-        <div class="row">
-            <div class="col-12 px-1 my-2">
-                <div class="col py-2 rounded border">
-                    <p>Please select a host type you wish to book. Only available types are shown.</p>
-                    {% bootstrap_field form.filter_field show_label=False %}
-                </div>
+        <div class="row mx-0 px-0">
+            <div class="col-12 mx-0 px-0 mt-2">
+                <p class="my-0">Please select a host type you wish to book. Only available types are shown.</p>
+                {% bootstrap_field form.filter_field show_label=False %}
             </div>
+        </div>
+        <div class="row">
             <div class="col-12 col-lg-3 px-1 my-2">
                 <div class="col border rounded py-2 h-100">
                     {% bootstrap_field form.purpose %}
     var sup_installer_dict = {{installer_filter | safe}};
     var sup_scenario_dict = {{scenario_filter | safe}};
 
-    function imageHider() {
+    function imageFilter() {
         var drop = document.getElementById("id_image");
-
-        hide_dropdown("id_image");
-
         var lab_pk = get_selected_value("lab");
         var host_pk = get_selected_value("host");
 
             var image_object = sup_image_dict[childNode.value];
             if (image_object) //weed out empty option
             {
-                if (image_object.host_profile == host_pk && image_object.lab == lab_pk) {
-                    childNode.style.display = "inherit";
-                    childNode.disabled = false;
-                }
+                childNode.disabled = !(image_object.host_profile == host_pk && image_object.lab == lab_pk);
             }
         }
     }
 
-    imageHider();
+    imageFilter();
     $('#id_installer').children().hide();
     $('#id_scenario').children().hide();
 
 
     Array.from(document.getElementsByClassName("grid-item-select-btn")).forEach(function (element) {
-        element.addEventListener('click', imageHider);
+        element.addEventListener('click', imageFilter);
     });
 
     function installerHider() {
index e97a41b..ad58ccb 100644 (file)
@@ -1,15 +1,15 @@
 <input name="filter_field" id="filter_field" type="hidden"/>
-<div id="grid_wrapper" class="container-fluid p-4">
-    <div class="row">
-        {% for object_class, object_list in display_objects %}
-            <div class="col-12 col-lg d-flex flex-column pt-2 my-2">
+<div class="row">
+    {% for object_class, object_list in display_objects %}
+        <div class="col-12 col-lg-6 d-flex flex-column pt-2 mx-0 px-1">
+            <div class="col mx-0 border rounded py-2 flex-grow-1 d-flex flex-column">
                 <div class="w-100">
                     <h4 class="text-capitalize">{{object_class}}</h4>
                 </div>
-                <div id="{{object_class}}" class="row h-100">
+                <div id="{{object_class}}" class="row flex-grow-1">
                 {% for obj in object_list %}
-                    <div class="col-12 col-md-6 col-xl-4 my-2">
-                        <div id="{{ obj.id|default:'not_provided' }}" class="card h-100" onclick="multi_filter_widget.processClick('{{obj.id}}');">
+                    <div class="col-12 col-md-6 col-xl-4 my-2 d-flex flex-grow-1">
+                        <div id="{{ obj.id|default:'not_provided' }}" class="card flex-grow-1">
                             <div class="card-header">
                                 <p class="h5 font-weight-bold mt-2">{{obj.name}}</p>
                             </div>
                                 <p class="grid-item-description">{{obj.description}}</p>
                             </div>
                             <div class="card-footer">
-                                <button type="button" class="btn btn-success grid-item-select-btn w-100">{% if obj.multiple %}Add{% else %}Select{% endif %}</button>
+                                <button type="button" class="btn btn-success grid-item-select-btn w-100 stretched-link"
+                                        onclick="multi_filter_widget.processClick('{{obj.id}}');">
+                                    {% if obj.multiple %}
+                                        Add
+                                    {% else %}
+                                        Select
+                                    {% endif %}
+                                </button>
                             </div>
                         </div>
                     </div>
                 {% endfor %}
                 </div>
             </div>
-        {% endfor %}
-    </div>
+        </div>
+    {% endfor %}
 </div>
 
 <div id="dropdown_wrapper" class="px-3 list-group-flush w-25 mt-2">
index 44689da..be51989 100644 (file)
@@ -25,7 +25,7 @@
     </input>
 
     <div id="scroll_restrictor" class="d-flex pb-4 position-relative">
-        <ul id="drop_results" class="list-group w-100 overflow-auto position-absolute"></ul>
+        <div id="drop_results" class="list-group w-100 z-2 overflow-auto position-absolute mh-30vh"></div>
     </div>
 </div>
 
index 33a7d8e..efd8a09 100644 (file)
@@ -7,9 +7,10 @@
 <p>Note that not all labs host every kind of machine.
 As you make your selections, labs and hosts that are not compatible
 with your current configuration will become unavailable.</p>
-<h4>NOTE: Only PTL's are able to create multi-node PODs. See <a href="https://google.com">here</a>
+<h4>NOTE: Only PTL's are able to create multi-node PODs. See
+    <a href="https://wiki.opnfv.org/display/INF/Lab-as-a-Service+at+the+UNH-IOL">here</a>
     for more details</h4>
-<form id="step_form" method="post">
+<form id="step_form" method="post" class="px-3">
     {% csrf_token %}
     {{form.filter_field|default:"<p>No Form</p>"}}
 </form>