Add visual cues to configuration panels
authorSimo Sorce <simo@redhat.com>
Thu, 6 Nov 2014 19:01:04 +0000 (14:01 -0500)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Wed, 12 Nov 2014 22:48:02 +0000 (23:48 +0100)
Make it easier to recognize which plugins are enabled and which are
disabled. Also make it easier to recognize when a plugin has just changed
state, by flashing its row (help also realize it may have moved up/down)

Based on concept work by Petr Vobornik

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/admin/common.py
less/admin.less
less/admin/variables.less [new file with mode: 0644]
less/admin/widgets.less [new file with mode: 0644]
templates/admin/plugins.html
templates/admin/providers.html
ui/css/admin.css

index af315ff..2b83314 100755 (executable)
@@ -141,13 +141,6 @@ class AdminPluginsOrder(AdminPage):
     def GET(self, *args, **kwargs):
         return self.parent.root_with_msg()
 
-    def _get_enabled_list(self):
-        cur = list()
-        for p in self._site[self.facility].available.values():
-            if p.is_enabled:
-                cur.append(p.name)
-        return cur
-
     @admin_protect
     def POST(self, *args, **kwargs):
 
@@ -158,7 +151,8 @@ class AdminPluginsOrder(AdminPage):
 
         message = "Nothing was modified."
         message_type = "info"
-        cur_enabled = self._get_enabled_list()
+        changed = None
+        cur_enabled = self._site[self.facility].enabled
 
         if 'order' in kwargs:
             order = kwargs['order'].split(',')
@@ -185,6 +179,12 @@ class AdminPluginsOrder(AdminPage):
                     message = "New configuration saved."
                     message_type = ADMIN_STATUS_OK
 
+                    changed = dict()
+                    self.debug('%s -> %s' % (cur_enabled, new_order))
+                    for i in range(0, len(cur_enabled)):
+                        if cur_enabled[i] != new_order[i]:
+                            changed[cur_enabled[i]] = 'reordered'
+
                 except ValueError, e:
                     message = str(e)
                     message_type = ADMIN_STATUS_ERROR
@@ -194,7 +194,8 @@ class AdminPluginsOrder(AdminPage):
                     message_type = ADMIN_STATUS_ERROR
 
         return self.parent.root_with_msg(message=message,
-                                         message_type=message_type)
+                                         message_type=message_type,
+                                         changed=changed)
 
 
 class AdminPlugins(AdminPage):
@@ -223,15 +224,19 @@ class AdminPlugins(AdminPage):
     def save_enabled_plugins(self, names):
         self._site[self.facility].save_enabled(names)
 
-    def root_with_msg(self, message=None, message_type=None):
+    def root_with_msg(self, message=None, message_type=None, changed=None):
         plugins = self._site[self.facility]
 
+        if changed is None:
+            changed = dict()
+
         targs = {'title': self.title,
                  'menu': self._master.menu,
                  'message': message,
                  'message_type': message_type,
                  'available': plugins.available,
                  'enabled': plugins.enabled,
+                 'changed': changed,
                  'baseurl': self.url,
                  'newurl': self.url}
         if self.order:
@@ -269,7 +274,8 @@ class AdminPlugins(AdminPage):
             obj.enable()
             obj.save_enabled_state()
             msg = "Plugin %s enabled" % obj.name
-        return self.root_with_msg(msg, ADMIN_STATUS_OK)
+        return self.root_with_msg(msg, ADMIN_STATUS_OK,
+                                  changed={obj.name: 'enabled'})
     enable.public_function = True
 
     @admin_protect
@@ -283,7 +289,8 @@ class AdminPlugins(AdminPage):
             obj.disable()
             obj.save_enabled_state()
             msg = "Plugin %s disabled" % obj.name
-        return self.root_with_msg(msg, ADMIN_STATUS_OK)
+        return self.root_with_msg(msg, ADMIN_STATUS_OK,
+                                  changed={obj.name: 'disabled'})
     disable.public_function = True
 
 
index 0b71139..f58f3ab 100644 (file)
 // Open Sans Fonts
 @import "font.less";
 
+// Admin Interface styles
+@import "admin/variables.less";
+@import "admin/widgets.less";
+
+
 // remove warnings
 @fa-var-angle-right: ">";
 @fa-var-angle-down: "\f107";
-
-// bigger default font
-@font-size-base:    14px;
diff --git a/less/admin/variables.less b/less/admin/variables.less
new file mode 100644 (file)
index 0000000..e30fd6c
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+// Basic variables
+@state-enabled-bg:  #ffffff;
+@state-disabled-bg:  #ededed;
+
+// bigger default font
+@font-size-base:    14px;
+
diff --git a/less/admin/widgets.less b/less/admin/widgets.less
new file mode 100644 (file)
index 0000000..fd68fed
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+// Widgets
+
+// imitate tables
+.ipsilon-row {
+
+    // reuse bootstrap colors
+    border-top: 1px solid @table-border-color;
+    padding: @table-cell-padding;
+
+    // to have equal height - basically a hack, increase if default elements are
+    // larger. Proper equal height would have to be done in JavaScript.
+    min-height: @input-height-large;
+}
+
+
+.ipsilon-row:last-child {
+    border-bottom: 1px solid @table-border-color;
+}
+
+// add to div with 'ipsilon-row' class to change background
+.hl-enabled {
+    // ugly color
+    background-color: @state-enabled-bg;
+}
+
+.hl-disabled {
+    background-color: @state-disabled-bg;
+
+    strong {
+        color: #555;
+    }
+}
+
+// animation
+// https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_animations
+
+@keyframes bgfadein {
+    from {
+        background-color: @state-disabled-bg;
+    }
+    to {
+        background-color: @state-enabled-bg;
+    }
+}
+
+@keyframes bgfadeout {
+    from {
+        background-color: @state-enabled-bg;
+    }
+    to {
+        background-color: @state-disabled-bg;
+    }
+}
+
+// add these classes to rows to hightlight them on load
+.hl-enabled-new {
+    animation-duration: 2s;
+    animation-name: bgfadein;
+    animation-fill-mode: both
+}
+
+.hl-disabled-new {
+    animation-duration: 2s;
+    animation-name: bgfadeout;
+    animation-fill-mode: both
+}
+
+@keyframes flashout {
+    0% {
+        background-color: @state-enabled-bg;
+    }
+    50% {
+        background-color: @state-info-bg;
+    }
+    100% {
+        background-color: @state-enabled-bg;
+    }
+}
+
+.hl-enabled-flash {
+    animation-duration: 1s;
+    animation-name: flashout;
+    animation-fill-mode: both
+}
index 7ef50a0..54462b7 100644 (file)
         </div>
     </div>
 
-    <table class="table">
     {% for p in enabled %}
-    <tr><td>
-      <div class="row">
+      {% set highlight = "hl-enabled" %}
+      {% if p in changed %}
+        {% if changed[p] == 'enabled' %}
+        {% set highlight = "hl-enabled-new" %}
+        {% elif changed[p] == 'reordered' %}
+        {% set highlight = "hl-enabled-flash" %}
+        {% endif %}
+      {% endif %}
+      <div class="row ipsilon-row {{ highlight }}">
         <div class="col-md-3 col-sm-3 col-xs-5">
-          <strong>{{ p }}</strong>
+          <p><strong>{{ p }}</strong></p>
         </div>
         <div class="col-md-7 col-sm-7 col-xs-5">
           <div class="row">
           <div class="col-md-6 col-sm-6 col-xs-12">
-              <a class="text-info" href="{{ baseurl }}/disable/{{ p }}">Disable</a>
+              <p class="text-info"><a href="{{ baseurl }}/disable/{{ p }}">Disable</a></p>
           </div>
           <div class="col-md-6 col-sm-6 col-xs-12">
             {%- if available[p].get_config_obj() %}
-              <a class="text-primary" href="{{ baseurl }}/{{ p }}">Configure</a>
+              <p class="text-primary"><a href="{{ baseurl }}/{{ p }}">Configure</a></p>
             {% endif %}
           </div>
           </div>
           {%- endif %}
         </div>
       </div>
-    </td></tr>
     {% endfor %}
 
     {% for p in available if not p in enabled %}
-    <tr><td>
-      <div class="row">
+      {% set highlight = "hl-disabled" %}
+      {% if p in changed %}
+        {% if changed[p] == 'disabled' %}
+        {% set highlight = "hl-disabled-new" %}
+        {% endif %}
+      {% endif %}
+      <div class="row ipsilon-row {{ highlight }}">
         <div class="col-md-3 col-sm-3 col-xs-5">
             <strong>{{ p }}</strong>
         </div>
         <div class="col-md-2 col-sm-2 col-xs-1">
         </div>
       </div>
-    </td></tr>
     {% endfor %}
 
-    <tr><td>
-    </td></tr>
-    </table>
-
 {% endif %}
 {% endblock %}
index 333b10d..a0ca765 100644 (file)
         </div>
     </div>
 
-    <hr>
-
-    {% for p in enabled %}
-      <div class="row">
+    {% for p in available %}
+      {%- if p in enabled %}
+        {%- set highlight = "hl-enabled" %}
+        {%- set actpath = "disable" %}
+        {%- set actlabel = "Disable" %}
+        {%- if p in changed %}
+          {%- if changed[p] == 'enabled' %}
+          {%- set highlight = "hl-enabled-new" %}
+          {%- endif %}
+        {%- endif %}
+      {%- else %}
+        {%- set highlight = "hl-disabled" %}
+        {%- set actpath = "enable" %}
+        {%- set actlabel = "Enable" %}
+        {%- if p in changed %}
+          {%- if changed[p] == 'disabled' %}
+          {%- set highlight = "hl-disabled-new" %}
+          {%- endif %}
+        {%- endif %}
+      {%- endif %}
+      <div class="row ipsilon-row {{ highlight }}">
         <div class="col-md-3 col-sm-3 col-xs-6">
           <p><strong>{{ p }}</strong></p>
         </div>
         <div class="col-md-9 col-sm-9 col-xs-6">
           <div class="row">
           <div class="col-md-4 col-sm-4 col-xs-12">
-              <p class="text-info"><a href="{{ baseurl }}/disable/{{ p }}">Disable</a></p>
+              <p class="text-info"><a href="{{ baseurl }}/{{ actpath }}/{{ p }}">{{ actlabel }}</a></p>
           </div>
           <div class="col-md-4 col-sm-4 col-xs-12">
             {%- if available[p].get_config_obj() %}
+              {%- if p in enabled %}
               <p class="text-primary"><a href="{{ baseurl }}/{{ p }}">Configure</a></p>
-            {% endif %}
-          </div>
-          <div class="col-md-4 col-sm-4 col-xs-12">
-            {%- if available[p].admin %}
-              <p class="text-primary"><a href="{{ baseurl }}/{{ p }}/admin">Manage</a></p>
-            {% endif %}
-          </div>
-          </div>
-        </div>
-      </div>
-      <hr>
-    {% endfor %}
-
-    {% for p in available if not p in enabled %}
-      <div class="row">
-        <div class="col-md-3 col-sm-3 col-xs-6">
-            <p><strong>{{ p }}</strong></p>
-        </div>
-        <div class="col-md-9 col-sm-9 col-xs-6">
-          <div class="row">
-          <div class="col-md-4 col-sm-4 col-xs-12">
-              <p class="text-info"><a href="{{ baseurl }}/enable/{{ p }}">Enable</a></p>
-          </div>
-          <div class="col-md-4 col-sm-4 col-xs-12">
-            {%- if available[p].get_config_obj() %}
+              {%- else %}
               <p class="text-muted">Configure</p>
-            {% endif %}
+              {%- endif %}
+            {%- endif %}
           </div>
           <div class="col-md-4 col-sm-4 col-xs-12">
             {%- if available[p].admin %}
+              {%- if p in enabled %}
+              <p class="text-primary"><a href="{{ baseurl }}/{{ p }}/admin">Manage</a></p>
+              {%- else %}
               <p class="text-muted">Manage</p>
+              {%- endif %}
             {% endif %}
           </div>
           </div>
         </div>
       </div>
-      <hr>
     {% endfor %}
 
 {% endif %}
index 2409613..0f9472a 100644 (file)
@@ -6068,3 +6068,62 @@ fieldset[disabled] .btn-primary.active {
   font-weight: 900;
   src: local('Open Sans Extrabold Italic'), url('../fonts/open-sans/OpenSans-ExtraBoldItalic.ttf') format('truetype');
 }
+.ipsilon-row {
+  border-top: 1px solid #dddddd;
+  padding: 8px;
+  min-height: 37px;
+}
+.ipsilon-row:last-child {
+  border-bottom: 1px solid #dddddd;
+}
+.hl-enabled {
+  background-color: #ffffff;
+}
+.hl-disabled {
+  background-color: #ededed;
+}
+.hl-disabled strong {
+  color: #555;
+}
+@keyframes bgfadein {
+  from {
+    background-color: #ededed;
+  }
+  to {
+    background-color: #ffffff;
+  }
+}
+@keyframes bgfadeout {
+  from {
+    background-color: #ffffff;
+  }
+  to {
+    background-color: #ededed;
+  }
+}
+.hl-enabled-new {
+  animation-duration: 3s;
+  animation-name: bgfadein;
+  animation-fill-mode: both;
+}
+.hl-disabled-new {
+  animation-duration: 3s;
+  animation-name: bgfadeout;
+  animation-fill-mode: both;
+}
+@keyframes flashout {
+  0% {
+    background-color: #ffffff;
+  }
+  50% {
+    background-color: #d9edf7;
+  }
+  100% {
+    background-color: #ffffff;
+  }
+}
+.hl-enabled-flash {
+  animation-duration: 1.5s;
+  animation-name: flashout;
+  animation-fill-mode: both;
+}