diff mbox

[32/51] series: Make the reviewer field editable

Message ID 1441986924-26689-33-git-send-email-damien.lespiau@intel.com
State Superseded
Headers show

Commit Message

Damien Lespiau Sept. 11, 2015, 3:55 p.m. UTC
Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
---
 htdocs/css/style.css                      |  12 ++-
 htdocs/js/patchwork.js                    | 153 +++++++++++++++++++++++++++++-
 patchwork/templates/patchwork/series.html |  37 +++++++-
 3 files changed, 198 insertions(+), 4 deletions(-)
diff mbox

Patch

diff --git a/htdocs/css/style.css b/htdocs/css/style.css
index 7588971..501a58f 100644
--- a/htdocs/css/style.css
+++ b/htdocs/css/style.css
@@ -243,14 +243,24 @@  input#reorder-change {
 	margin: 0.2em 0;
 }
 
+/* common to patch and series */
+span.glyphicon-pencil {
+	cursor: pointer;
+}
+
+div.editable-field div.selectize-input {
+	width: 300px;
+}
+
 /* patch view */
 table.patchmeta th {
 	text-align: left;
+        height: 30px;
 }
 
 table.patchmeta tr th, table.patchmeta tr td {
 	text-align: left;
-	padding: 3px 10px 3px 10px;
+	padding: 0 10px;
 	vertical-align: middle;
 }
 
diff --git a/htdocs/js/patchwork.js b/htdocs/js/patchwork.js
index b7c4ad3..c1dfa6a 100644
--- a/htdocs/js/patchwork.js
+++ b/htdocs/js/patchwork.js
@@ -2,9 +2,26 @@  var pw = (function() {
     var _this,
         exports = {},
         ctx = {
-            project: null
+            project: null,
+            series: null
         };
 
+    function get_cookie(name) {
+        var value = null;
+        if (document.cookie && document.cookie != '') {
+            var cookies = document.cookie.split(';');
+            for (var i = 0; i < cookies.length; i++) {
+                var cookie = jQuery.trim(cookies[i]);
+                // Does this cookie string begin with the name we want?
+                if (cookie.substring(0, name.length + 1) == (name + '=')) {
+                    value = decodeURIComponent(cookie.substring(name.length + 1));
+                    break;
+                }
+            }
+        }
+        return value;
+    }
+
     var columnsMap = {
         'Series': 'name',
         'Patches': 'n_patches',
@@ -39,6 +56,8 @@  var pw = (function() {
 
         this.amend_context(init_ctx);
 
+        ctx.csrftoken = get_cookie('csrftoken');
+
         $.dynatableSetup({
             dataset: {
                 perPageDefault: 20,
@@ -118,5 +137,137 @@  var pw = (function() {
         }
     }
 
+    function setup_autocompletion(field) {
+        var $s = $('#edit-' + field.name + ' input');
+        var s = $s[0].selectize;
+
+        if (!field.autocomplete)
+            return;
+
+        if (s)
+            return;
+
+        $s.selectize({
+            valueField: 'pk',
+            labelField: field.autocomplete.display_field,
+            searchField: field.autocomplete.search_fields,
+            maxItems: 1,
+            persist: false,
+            openOnFocus: false,
+            render: {
+                option: field.autocomplete.render || function(item, escape) {
+                        return '<div>' + escape(item[field.autocomplete.display_field]) + '</div>';
+                },
+                item: field.autocomplete.render || function(item, escape) {
+                        return '<div>' + escape(item[field.autocomplete.display_field]) + '</div>';
+                }
+            },
+            load: function(query, callback) {
+                if (query.length < 3)
+                    return callback();
+
+                req = $.ajax({
+                    url: field.autocomplete.url + '/?q=' + encodeURIComponent(query) + '&l=10',
+                    error: function() {
+                        callback();
+                    },
+                    success: function(res) {
+                        callback(res);
+                    }
+                });
+            }
+        });
+
+        s = $s[0].selectize;
+        s.on('item_add', function(value) {
+            $('#save-' + field.name).removeClass('disabled');
+        });
+
+        s.on('item_remove', function(value) {
+            $('#save-' + field.name).removeClass('disabled');
+        });
+
+    }
+
+    function cleanup_autocompletion(field) {
+        var $s = $('#edit-' + field.name + ' input');
+        var s = $s[0].selectize;
+
+        s.destroy();
+        delete $s[0].selectize;
+    }
+
+    function field_setup(field) {
+        /* what to do when the edit icon is clicked */
+        $('#field-' + field.name + ' .glyphicon-pencil').click(function() {
+            var input = $('#edit-' + field.name + ' input');
+
+            $(this).parent().hide();
+            $('#save-' + field.name).addClass('disabled');
+            $('#edit-' + field.name).show();
+
+            setup_autocompletion(field);
+
+            if (field.value) {
+                input[0].selectize.addOption(field.value);
+                input[0].selectize.setValue(field.value.pk);
+            }
+            input[0].selectize.focus();
+        });
+
+        function field_refresh(field) {
+            var content;
+
+            if (field.is_null())
+                content = '<em class="text-muted">None</em>';
+            else
+                content = field.text();
+
+            $('#field-' + field.name + '-text').html(content);
+        }
+
+        /* the save button is clicked */
+        $('#save-' + field.name).click(function() {
+            var $s = $('#edit-' + field.name + ' input');
+            var s = $s[0].selectize;
+            var val = parseInt(s.getValue()) || null;
+            var patch_data = {};
+
+            patch_data[field.name] = val;
+
+            $.ajax({
+                url: '/api/1.0/series/' + ctx.series + '/',
+                headers: {
+                    'X-HTTP-Method-Override': 'PATCH',
+                    'X-CSRFToken': ctx.csrftoken
+                },
+                type: 'POST',
+                data: patch_data,
+                success: function(response) {
+                    field.init_from_series(response);
+                    field_refresh(field);
+                    $('#edit-' + field.name).hide();
+                    $('#field-' + field.name).show();
+                },
+                /* TODO: show the error to the user, maybe use the 'details'
+                 * from the response */
+                error: function(ctx, status, error) {
+                    console.log("Couldn't save " + field.name + ": " + status, error);
+                }
+            })
+        });
+
+        /* the cancel button is clicked */
+        $('#cancel-' + field.name).click(function() {
+            $('#edit-' + field.name).hide();
+            $('#field-' + field.name).show();
+        });
+
+    }
+
+    exports.setup_editable_fields = function(fields) {
+        fields.forEach(field_setup);
+    }
+
     return exports
 }());
diff --git a/patchwork/templates/patchwork/series.html b/patchwork/templates/patchwork/series.html
index 31d779b..3826647 100644
--- a/patchwork/templates/patchwork/series.html
+++ b/patchwork/templates/patchwork/series.html
@@ -8,6 +8,25 @@ 
 <script language="JavaScript" type="text/javascript">
 $(function () {
     pw.setup_series({ patches: 'series-patchlist' });
+    pw.amend_context({ series: {{ series.pk }} });
+    pw.setup_editable_fields([{
+        name: 'reviewer'
+{% if series.reviewer %}
+        ,value: { pk: {{ series.reviewer.pk }},
+                  display_name: "{{ series.reviewer.name }}" }
+{% else %}
+        ,value: { pk: null, name: null }
+{% endif %}
+        ,init_from_series: function(o) {
+            this.value.pk = o.reviewer;
+            this.value.display_name = o.reviewer__name;
+        }
+        ,is_null: function() { return !this.value.pk; }
+        ,text: function() { return this.value.display_name; }
+        ,autocomplete: { url: '/complete_user',
+                         display_field: 'display_name',
+                         search_fields: ['username', 'display_name'] }
+    }]);
 });
 </script>
 {% endblock %}
@@ -27,11 +46,25 @@  $(function () {
 <table class="patchmeta">
   <tr>
     <th>Reviewer</th>
+    <td>
+      <span id="field-reviewer">
+        <span id="field-reviewer-text">
 {% if series.reviewer %}
-    <td>{{ series.reviewer }}</td>
+          {{ series.reviewer.name }}
 {% else %}
-    <td><em class="text-muted">None</em></td>
+        <em class="text-muted">None</em>
 {% endif %}
+        </span>
+        <span class="glyphicon glyphicon-pencil" ></span>
+      </span>
+      <div id="edit-reviewer" class="form-inline" style="display:none;">
+        <div class="form-group">
+          <input type="text" class="form-control input-sm editable-field">
+          <button id="save-reviewer" type="button" class="btn btn-primary btn-sm">Save</button>
+          <button id="cancel-reviewer" type="button" class="btn btn-default btn-sm">Cancel</button>
+        </div>
+      </div>
+    </td>
   </tr>
   <tr>
     <th>Submitted</th>