Преглед изворни кода

Added panelMeta object for extra panel details, moved add panel button, tweaked editors

Rashid Khan пре 12 година
родитељ
комит
13fc14ffa9

+ 1 - 0
Gruntfile.js

@@ -18,6 +18,7 @@ module.exports = function (grunt) {
       files: ['Gruntfile.js', 'js/*.js', 'panels/*/*.js' ],
       options: {
         bitwise: true,
+        maxlen: 140,
         curly: true,
         eqeqeq: true,
         immed: true,

+ 1 - 0
js/controllers.js

@@ -129,6 +129,7 @@ angular.module('kibana.controllers', [])
   };
 
   $scope.reset_panel = function() {
+
     $scope.panel = {
       loading : false,
       error   : false,

+ 7 - 3
js/directives.js

@@ -8,8 +8,9 @@ angular.module('kibana.directives', [])
     restrict: 'E',
     link: function(scope, elem, attrs) {
       var template = '<img src="common/img/load.gif" class="panel-loading" ng-show="panel.loading == true">'+
-        ' <span class="editlink panelextra pointer" style="right:15px;top:0px" bs-modal="\'partials/paneleditor.html\'" ng-show="panel.editable != false">'+
-          '<span class="small">{{panel.type}}</span> <i class="icon-cog pointer"></i> '+
+        ' <span class="editlink panelextra pointer" style="right:15px;top:0px" ' + 
+        'bs-modal="\'partials/paneleditor.html\'" ng-show="panel.editable != false">'+
+        '<span class="small">{{panel.type}}</span> <i class="icon-cog pointer"></i> '+
         '</span><h4>{{panel.title}}</h4>';
       elem.prepend($compile(angular.element(template))(scope));
     }
@@ -21,7 +22,10 @@ angular.module('kibana.directives', [])
     link: function(scope, elem, attrs) {
       scope.$watch('panel.type', function(n,o) {
         if(!_.isUndefined(scope.panel.type)) {
-          var template = '<div ng-controller="'+scope.panel.type+'" ng-include src="\''+scope.edit_path(scope.panel.type)+'\'"></div>';
+          var template = '<div>'+
+          '<div ng-controller="'+scope.panel.type+'" ng-include src="\'partials/panelgeneral.html\'"></div>'+
+          '<div ng-controller="'+scope.panel.type+'" ng-include src="\''+scope.edit_path(scope.panel.type)+'\'">'+
+          '</div>';
           elem.html($compile(angular.element(template))(scope));
         }
       });

+ 1 - 8
panels/bettermap/editor.html

@@ -1,14 +1,7 @@
-  <div class="row-fluid">
-    <div class="span11">
-    This panel uses geoJSON points in a field to place markers on a map. 
-    Coordinates <strong>must be stored as an array in Elasticsearch</strong>. 
-    Also note that geoJSON is <strong>long,lat NOT lat,long</strong>.
-    </div>
-  </div>
   <div class="row-fluid">    
     <div class="span4">
       <form>
-        <h6>Coordinate Field</h6>
+        <h6>Coordinate Field <i class="icon-question-sign" bs-tooltip="'geoJSON array! Long,Lat NOT Lat,Long'"></i></h6>
         <input  bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.field">
       </form>
     </div>

+ 10 - 7
panels/bettermap/module.js

@@ -5,10 +5,6 @@
 
   ## Better maps
 
-  So the cavaet for this panel is that, for better or worse, it does NOT use the terms facet and it
-  DOES query sequentially. This however means that it transfer more data and is generally heavier
-  to computer, while showing less actual data
-
   ### Parameters
   * size :: How many results to show, more results = slower
   * field :: field containing a 2 element array in the format [lon,lat]
@@ -21,9 +17,17 @@
 angular.module('kibana.bettermap', [])
 .controller('bettermap', function($scope, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Experimental",
+    description : "Displays geo points in clustered groups on a map. The cavaet for this panel is"+
+      " that, for better or worse, it does NOT use the terms facet and it <b>does</b> query "+
+      "sequentially. This however means that it transfers more data and is generally heavier to"+
+      " compute, while showing less actual data. If you have a time filter, it will attempt to"+
+      " show to most recent points in your search, up to your defined limit"
+  };
+
   // Set and populate defaults
   var _d = {
-    status  : "Experimental",
     queries     : {
       mode        : 'all',
       ids         : []
@@ -31,8 +35,7 @@ angular.module('kibana.bettermap', [])
     size    : 1000,
     spyable : true,
     tooltip : "_id",
-    field   : null,
-    group   : "default"
+    field   : null
   };
   _.defaults($scope.panel,_d);
 

+ 7 - 5
panels/column/module.js

@@ -4,10 +4,6 @@
 
   ## Column
 
-  The column panel is sort of a hack to allow you to put multiple, veritcal, 
-  panels next to a bigger panel. Note that it has no group, and setting a group
-  for the panel itself will do nothing
-
   ### Parameters
   * panels :: an array of panel objects. All of their spans should be set to 12
   
@@ -17,9 +13,15 @@
 
 angular.module('kibana.column', [])
 .controller('column', function($scope, $rootScope, $timeout) {
+
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "A pseudo panel that lets you add other panels to be arranged in a column with"+
+      "defined heights."
+  };
+
   // Set and populate defaults
   var _d = {
-    status: "Stable",
     panels : []
   };
   _.defaults($scope.panel,_d);

+ 7 - 7
panels/dashcontrol/module.js

@@ -5,10 +5,6 @@
 
   ## Dashcontrol
 
-  Dash control allows for saving, loading and sharing of dashboards. Do not
-  disable the dashcontrol module as a special instance of it allows for loading
-  the default dashboard from dashboards/default
-
   ### Parameters
   * save
   ** gist :: Allow saving to gist. Requires registering an oauth domain with Github
@@ -30,11 +26,14 @@
 angular.module('kibana.dashcontrol', [])
 .controller('dashcontrol', function($scope, $http, timer, dashboard) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "This panel allows for saving, loading, exporting and sharing dashboard schemas."
+  };
+
   $scope.panel = $scope.panel || {};
   // Set and populate defaults
   var _d = {
-    status  : "Stable",
-    group   : "default",
     save : {
       gist: false,
       elasticsearch: true,
@@ -138,7 +137,8 @@ angular.module('kibana.dashcontrol', [])
       function(link) {
       if(!_.isUndefined(link)) {
         $scope.gist.last = link;
-        $scope.alert('Gist saved','You will be able to access your exported dashboard file at <a href="'+link+'">'+link+'</a> in a moment','success');
+        $scope.alert('Gist saved','You will be able to access your exported dashboard file at '+
+          '<a href="'+link+'">'+link+'</a> in a moment','success');
       } else {
         $scope.alert('Save failed','Gist could not be saved','error',5000);
       }

+ 0 - 6
panels/derivequeries/editor.html

@@ -17,10 +17,4 @@
       <input array-join type="text" style="width:90%" ng-change="set_refresh(true)" ng-model='panel.exclude'></input>
     </div>
   </div>
-  <div class="row-fluid">    
-    <div class="span12">
-      The derive queries panel takes a query and a field, runs a terms facet, then creates queries based on them. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p>
-      Query Mode allows to optionally append original query to each term in the list.
-    </div>
-  </div>
 </div>

+ 9 - 4
panels/derivequeries/module.js

@@ -4,8 +4,6 @@
 
   ## Derivequeries
 
-  Broadcasts an array of queries based on the results of a terms facet
-
   ### Parameters
   * label :: The label to stick over the field 
   * query :: A string to use as a filter for the terms facet
@@ -21,14 +19,21 @@
 angular.module('kibana.derivequeries', [])
 .controller('derivequeries', function($scope, $rootScope, querySrv, fields, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Experimental",
+    description : "Creates a new set of queries using the Elasticsearch terms facet. For example,"+
+     " you might want to create 5 queries showing the most frequent HTTP response codes. Be "+
+     "careful not to select a high cardinality field, as Elasticsearch must load all unique values"+
+     " into memory."
+  };
+
+
   // Set and populate defaults
   var _d = {
     loading : false,
-    status  : "Beta",
     label   : "Search",
     query   : "*",
     ids     : [],
-    group   : "default",
     field   : '_type',
     fields  : [],
     spyable : true,

+ 8 - 3
panels/fields/module.js

@@ -5,8 +5,6 @@
 
   ## Fields
 
-  Allows for enabling and disabling of fields in the table panel as well as a 
-  micro anaylsis panel for analyzing the events in the table panel
 
   ### Parameters
   * style :: a hash containing css styles
@@ -26,9 +24,16 @@
 angular.module('kibana.fields', [])
 .controller('fields', function($scope, eventBus, $timeout, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Deprecating Soon",
+    description : "Allows for enabling and disabling of fields in the table panel as well as a "+
+      "micro anaylsis panel for analyzing the events in the table panel. This panel will soon be"+
+      "combined with the table panel"
+  };
+
+
   // Set and populate defaults
   var _d = {
-    status  : "Beta",
     group   : "default",
     style   : {},
     arrange : 'vertical',

+ 6 - 3
panels/filtering/module.js

@@ -4,8 +4,6 @@
 
   ## filtering
 
-  An experimental for interacting with the filter service
-
 */
 
 'use strict';
@@ -13,9 +11,14 @@
 angular.module('kibana.filtering', [])
 .controller('filtering', function($scope, filterSrv, $rootScope, dashboard) {
 
+  $scope.panelMeta = {
+    status  : "Beta",
+    description : "A controllable list of all filters currently applied to the dashboard. You "+
+      "almost certainly want one of these on your dashboard somewhere."
+  };
+
   // Set and populate defaults
   var _d = {
-    status  : "Beta"
   };
   _.defaults($scope.panel,_d);
 

+ 7 - 5
panels/histogram/module.js

@@ -5,10 +5,6 @@
 
   ## Histogram
 
-  A bucketted time series representation of the current query or queries. Note that this
-  panel uses facetting. I tried to make it safe by using sequential/serial querying but,
-  yeah, you should know that it uses facetting. It should be pretty safe.
-
   ### Parameters
   * auto_int :: Auto calculate data point interval?
   * resolution ::  If auto_int is enables, shoot for this many data points, rounding to
@@ -40,9 +36,15 @@
 angular.module('kibana.histogram', [])
 .controller('histogram', function($scope, eventBus, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "A bucketed time series chart of the current query or queries. Uses the "+
+      "Elasticsearch date_histogram facet. If using time stamped indices this panel will query"+
+      " them sequentially to attempt to apply the lighest possible load to your Elasticsearch cluster"
+  };
+
   // Set and populate defaults
   var _d = {
-    status      : "Stable",
     mode        : 'count',
     time_field  : '@timestamp',
     queries     : {

+ 8 - 4
panels/hits/module.js

@@ -5,8 +5,6 @@
 
   ## Hits
 
-  A variety of representations of the hits a query matches
-
   ### Parameters
   * style :: A hash of css styles
   * arrangement :: How should I arrange the query results? 'horizontal' or 'vertical'
@@ -22,9 +20,14 @@
 angular.module('kibana.hits', [])
 .controller('hits', function($scope, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "The total hits for a query or set of queries. Can be a pie chart, bar chart, "+
+      "list, or absolute total of all queries combined"
+  };
+
   // Set and populate defaults
   var _d = {
-    status  : "Beta",
     queries     : {
       mode        : 'all',
       ids         : []
@@ -219,7 +222,8 @@ angular.module('kibana.hits', [])
                       show: scope.panel.labels,
                       radius: 2/3,
                       formatter: function(label, series){
-                        return '<div ng-click="build_search(panel.query.field,\''+label+'\') "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
+                        return '<div ng-click="build_search(panel.query.field,\''+label+'\')'+
+                          ' "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
                           label+'<br/>'+Math.round(series.percent)+'%</div>';
                       },
                       threshold: 0.1 

+ 0 - 5
panels/map/editor.html

@@ -21,9 +21,4 @@
     <div class="span2"> 
       <label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
     </div>
-    <div class="span9 small">
-      The panel spy shows 'behind the scenes' information about a panel. It can
-      be accessed by clicking the <i class='icon-eye-open'></i> in the top right
-      of the panel.
-    </div>
   </div>

+ 7 - 7
panels/map/module.js

@@ -5,12 +5,6 @@
 
   ## Map
 
-  LOL. Should this even be documented? Zach's map panel is going to ruin this one. 
-  For serious. This shades a map of the world, the US or Europe with the number of 
-  events that match the query. Uses 2 letter country codes and nothing else. This uses
-  a terms facet. Its probably safe as long as you point it at the right field. Nach.
-  There's no way to query sequentially here, so I'm going to hit them all at once!
-
   ### Parameters
   * map :: 'world', 'us' or 'europe'
   * colors :: an array of colors to use for the regions of the map. If this is a 2 
@@ -28,9 +22,15 @@
 angular.module('kibana.map', [])
 .controller('map', function($scope, $rootScope, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "Displays a map of shaded regions using a field containing a 2 letter country "+
+     ", or US state, code. Regions with more hit are shaded darker. Node that this does use the"+
+     " Elasticsearch terms facet, so it is important that you set it to the correct field."
+  };
+
   // Set and populate defaults
   var _d = {
-    status  : "Beta",
     queries     : {
       mode        : 'all',
       ids         : []

+ 9 - 9
panels/pie/module.js

@@ -4,12 +4,6 @@
 
   ## Pie
 
-  This panel is probably going away. For now its has 2 modes: 
-    * terms: Run a terms facet on the query. You're gonna have a bad (ES crashing) day
-    if you run in this mode on a high cardinality field
-    * goal: Compare the query to this number and display the percentage that the query
-    represents
-
   ### Parameters
   * query :: An object with 2 possible parameters depends on the mode:
   ** field: Fields to run a terms facet on. Only does anything in terms mode
@@ -30,9 +24,15 @@
 angular.module('kibana.pie', [])
 .controller('pie', function($scope, $rootScope, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Deprecating Soon",
+    description : "Uses an Elasticsearch terms facet to create a pie chart. You should really only"+
+      " point this at not_analyzed fields for that reason. This panel is going away soon, to be"+
+      " replaced with a panel that can represent a terms facet in a variety of ways."
+  };
+
   // Set and populate defaults
   var _d = {
-    status  : "Deprecating Soon",
     query   : { field:"_type", goal: 100}, 
     queries     : {
       mode        : 'all',
@@ -45,7 +45,6 @@ angular.module('kibana.pie', [])
     legend  : "above",
     labels  : true,
     mode    : "terms",
-    group   : "default",
     default_field : 'DEFAULT',
     spyable : true,
   };
@@ -291,7 +290,8 @@ angular.module('kibana.pie', [])
       elem.bind("plothover", function (event, pos, item) {
         if (item) {
           var percent = parseFloat(item.series.percent).toFixed(1) + "%";
-          tt(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " + 
+          tt(pos.pageX, pos.pageY, "<div "+"style='vertical-align:middle;display:inline-block;"+
+            "background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> "+
             (item.series.label||"")+ " " + percent);
         } else {
           $("#pie-tooltip").remove();

+ 6 - 4
panels/query/module.js

@@ -4,8 +4,6 @@
 
   ## query
 
-  An experimental panel for the query service
-
   ### Parameters
   * label ::  The label to stick over the field 
   * query ::  A string or an array of querys. String if multi is off, array if it is on
@@ -18,13 +16,17 @@
 angular.module('kibana.query', [])
 .controller('query', function($scope, querySrv, $rootScope) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "Manage all of the queries on the dashboard. You almost certainly need one of "+
+      "these somewhere. This panel allows you to add, remove, label, pin and color queries"
+  };
+
   // Set and populate defaults
   var _d = {
-    status  : "Beta",
     label   : "Search",
     query   : "*",
     pinned  : true,
-    group   : "default",
     history : [],
     remember: 10 // max: 100, angular strap can't take a variable for items param
   };

+ 6 - 2
panels/table/module.js

@@ -4,8 +4,6 @@
 
   ## Table
 
-  A paginated table of events matching a query
-
   ### Parameters
   * size :: Number of events per page to show
   * pages :: Number of pages to show. size * pages = number of cached events. 
@@ -31,6 +29,12 @@
 angular.module('kibana.table', [])
 .controller('table', function($rootScope, $scope, eventBus, fields, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status: "Stable",
+    description: "A paginated table of records matching your query or queries. Click on a row to "+
+      "expand it and review all of the fields associated with that document. <p>"
+  };
+
   // Set and populate defaults
   var _d = {
     status  : "Stable",

+ 6 - 3
panels/text/module.js

@@ -5,8 +5,6 @@
 
   ## Text
 
-  A simple panel of static content
-
   ### Parameters
   * mode :: 'text', 'html', 'markdown'
   * content :: Content of the panel
@@ -19,10 +17,15 @@
 angular.module('kibana.text', [])
 .controller('text', function($scope, $rootScope) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "A static text panel that can use plain text, markdown, or (sanitized) HTML"
+  };
+
+
   // Set and populate defaults
   var _d = {
     status  : "Stable",
-    group   : "default",
     mode    : "markdown",
     content : "",
     style: {},

+ 7 - 5
panels/timepicker/module.js

@@ -4,10 +4,6 @@
 
   ## Timepicker
 
-  The timepicker panel is used to select time ranges and inform other panel of 
-  them. It also handles searching for indices that match the given time range and 
-  a pattern
-
   ### Parameters
   * mode :: The default mode of the panel. Options: 'relative', 'absolute' 'since' Default: 'relative'
   * time_options :: An array of possible time options. Default: ['5m','15m','1h','6h','12h','24h','2d','7d','30d']
@@ -24,6 +20,13 @@
 angular.module('kibana.timepicker', [])
 .controller('timepicker', function($scope, $rootScope, $timeout, timer, $http, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Stable",
+    description : "A panel for controlling the time range filters. If you have time based data, "+
+      " or if you're using time stamped indices, you need one of these"
+  };
+
+
   // Set and populate defaults
   var _d = {
     status        : "Stable",
@@ -32,7 +35,6 @@ angular.module('kibana.timepicker', [])
     timespan      : '15m',
     timefield     : '@timestamp',
     timeformat    : "",
-    group         : "default",
     refresh       : {
       enable  : false, 
       interval: 30,

+ 9 - 4
panels/trends/module.js

@@ -4,8 +4,6 @@
 
   ## Trends
 
-  Shows how queries are moving from a specified time ago
-
   ### Parameters
   * style :: A hash of css styles
   * arrangement :: How should I arrange the query results? 'horizontal' or 'vertical'
@@ -18,14 +16,21 @@
 angular.module('kibana.trends', [])
 .controller('trends', function($scope, kbnIndex, querySrv, dashboard, filterSrv) {
 
+  $scope.panelMeta = {
+    status  : "Beta",
+    description : "A stock-ticker style representation of how queries are moving over time. "+
+    "For example, if the time is 1:10pm, your time picker was set to \"Last 10m\", and the \"Time "+
+    "Ago\" parameter was set to '1h', the panel would show how much the query results have changed"+
+    " since 12:00-12:10pm"
+  };
+
+
   // Set and populate defaults
   var _d = {
-    status  : "Beta",
     queries     : {
       mode        : 'all',
       ids         : []
     },
-    group   : "default",
     style   : { "font-size": '14pt'},
     ago     : '1d',
     arrangement : 'vertical',

+ 1 - 1
partials/dasheditor.html

@@ -89,5 +89,5 @@
   </div>
 </div>
 <div class="modal-footer">
-  <button type="button" class="btn btn-success" ng-click="editor.index=0;dismiss();reset_panel();dashboard.refresh()">Close</button>
+  <button type="button" class="btn btn-danger" ng-click="editor.index=0;dismiss();reset_panel();dashboard.refresh()">Close</button>
 </div>

+ 2 - 2
partials/paneleditor.html

@@ -1,5 +1,5 @@
 <div class="modal-body" >
-  <div class="pull-right editor-title"><small>({{panel.status}})</small> {{panel.type}} settings</div>
+  <div class="pull-right editor-title">{{panel.type}} settings</div>
 
   <div ng-model="editor.index" bs-tabs>
     <div ng-repeat="tab in ['General','Panel']" data-title="{{tab}}">
@@ -16,5 +16,5 @@
 </div>
 <div class="modal-footer">
   <!-- close_edit() is provided here to allow for a scope to perform action on dismiss -->
-  <button type="button" class="btn btn-success" ng-click="editor.index=0;close_edit();dismiss()">Close</button>
+  <button type="button" class="btn btn-danger" ng-click="editor.index=0;close_edit();dismiss()">Close</button>
 </div>

+ 6 - 1
partials/panelgeneral.html

@@ -1,3 +1,8 @@
+  <div class="row-fluid">
+    <div class="span12">
+      <strong>{{panelMeta.status}}</strong> // <span ng-bind-html="panelMeta.description"></span>
+    </div>
+  </div>
   <div class="row-fluid">
     <div class="span4">
       <label class="small">Title</label><input type="text" class="input-medium" ng-model='panel.title'></input>
@@ -5,7 +10,7 @@
     <div class="span2" ng-hide="panel.sizeable == false">
       <label class="small">Span</label> <select class="input-mini" ng-model="panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
     </div>
-    <div class="span1"> 
+    <div class="span2"> 
       <label class="small">Editable</label><input type="checkbox" ng-model="panel.editable" ng-checked="panel.editable">
     </div>
   </div>

+ 13 - 14
partials/roweditor.html

@@ -2,7 +2,7 @@
   <div class="pull-right editor-title">Row settings</div>
 
   <div ng-model="editor.index" bs-tabs>
-    <div ng-repeat="tab in ['General','Panels']" data-title="{{tab}}">
+    <div ng-repeat="tab in ['General','Panels','Add Panel']" data-title="{{tab}}">
     </div>
   </div>
 
@@ -20,18 +20,6 @@
       <label class="small"> Collapsable </label><input type="checkbox" ng-model="row.collapsable" ng-checked="row.collapsable" />
     </div>
   </div>
-  <div class="row-fluid" ng-show="editor.index == 1">
-    <h4>New Panel</h4>
-    <form class="input-append">
-    <select class="input-medium input-append" ng-model="panel.type" ng-options="f for f in config.modules|stringSort"></select>
-    <small ng-show="!panel.type">Select Type</small> <button ng-show="panel.type" ng-click="add_panel(row,panel); reset_panel();" class="btn btn-success ">Add <i class="icon-plus"></i></button></form>
-
-    <div ng-show="!(_.isUndefined(panel.type))">
-      <div ng-include src="'partials/panelgeneral.html'"></div>
-
-      <div add-panel="{{panel.type}}"></div>
-    </div>
-  </div>
   <div class="row-fluid" ng-show="editor.index == 1">
     <div class="span12">
       <h4>Panels</h4>
@@ -57,7 +45,18 @@
       </table>
     </div>
   </div>
+  <div class="row-fluid" ng-show="editor.index == 2">
+    <h4>Select Panel Type</h4>
+    <form class="input-append">
+    <select class="input-medium input-append" ng-model="panel.type" ng-options="f for f in config.modules|stringSort"></select>
+    <small ng-show="!panel.type">Select Type</small></form>
+
+    <div ng-show="!(_.isUndefined(panel.type))">
+      <div add-panel="{{panel.type}}"></div>
+    </div>
+  </div>
 </div>
 <div class="modal-footer">
-  <button type="button" class="btn btn-success" ng-click="editor.index=0;dismiss();reset_panel();close_edit()">Close</button>
+  <button ng-show="panel.type && editor.index == 2" ng-click="add_panel(row,panel); reset_panel(); editor.index == 1;" class="btn btn-success ">Add Panel</button>
+  <button type="button" class="btn btn-danger" ng-click="editor.index=0;dismiss();reset_panel();close_edit()">Close</button>
 </div>