Browse Source

HUE-5563 [responsive] Add the context panel and display sessions for the editor

Johan Ahlen 8 years ago
parent
commit
2378602cc0

+ 34 - 0
desktop/core/src/desktop/static/desktop/css/hue.css

@@ -1456,3 +1456,37 @@ a:visited {
 .ui-autocomplete {
   z-index: 3000 !important;
 }
+/* context panel */
+/* TODO: Positioning and clean-up */
+.context-panel {
+  position: fixed;
+  background: #FFFFFF;
+  height: calc(30%);
+  top: 113px;
+  z-index: 401;
+  overflow-y: auto;
+  padding: 10px 0;
+  width: 420px;
+  right: -440px;
+  outline: none !important;
+  -webkit-box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.18);
+  -moz-box-shadow: -2px 2px 8px rgba(0, 0, 0, 0.18);
+  box-shadow: -2px 2px 6px 0 rgba(0, 0, 0, 0.18), -2px 2px 8px 0 rgba(0, 0, 0, 0.13);
+  -webkit-transition: right 0.2s ease;
+  -moz-transition: right 0.2s ease;
+  -ms-transition: right 0.2s ease;
+  transition: right 0.2s ease;
+}
+.context-panel.visible {
+  right: 0;
+}
+.context-panel .nav-tabs {
+  padding-left: 10px;
+}
+.context-panel .tab-content {
+  padding: 12px;
+  min-height: calc(10%);
+}
+.context-panel .tab-pane {
+  height: 100%;
+}

+ 34 - 0
desktop/core/src/desktop/static/desktop/less/hue.less

@@ -895,3 +895,37 @@ a:visited {
 .tooltip, .ui-autocomplete {
   z-index: 3000 !important;
 }
+
+/* context panel */
+/* TODO: Positioning and clean-up */
+.context-panel {
+  position: fixed;
+  background: @cui-white;
+  height: calc(30%);
+  top: 113px;
+  z-index: 401;
+  overflow-y: auto;
+  padding: 10px 0;
+  width: 420px;
+  right: -440px;
+  outline: none !important;
+  .hue-box-shadow(-2px, 2px);
+  .hue-ease-transition(right);
+}
+
+.context-panel.visible {
+  right: 0;
+}
+
+.context-panel .nav-tabs {
+  padding-left: 10px;
+}
+
+.context-panel .tab-content {
+  padding: 12px;
+  min-height: calc(10%);
+}
+
+.context-panel .tab-pane {
+  height: 100%;
+}

+ 3 - 7
desktop/core/src/desktop/templates/assist.mako

@@ -1744,11 +1744,6 @@ from notebook.conf import ENABLE_QUERY_BUILDER
           }
         });
 
-        self.activeType.subscribe(function (newType) {
-          console.log(newType);
-          self.selectedFunction(selectedFunctionPerType[newType]);
-        });
-
         self.activeCategories = ko.observable();
 
         self.filteredCategories = ko.pureComputed(function () {
@@ -1760,8 +1755,9 @@ from notebook.conf import ENABLE_QUERY_BUILDER
           })
         });
 
-        self.activeType.subscribe(function (newValue) {
-          self.activeCategories(self.categories[newValue]);
+        self.activeType.subscribe(function (newType) {
+          self.selectedFunction(selectedFunctionPerType[newType]);
+          self.activeCategories(self.categories[newType]);
         });
 
         self.activeType(self.availableTypes()[0]);

+ 66 - 17
desktop/core/src/desktop/templates/responsive.mako

@@ -384,9 +384,9 @@ ${ hueIcons.symbols() }
 
       <div data-bind="visible: rightAssistVisible" style="display: none; height: 100%; width: 100%; position: relative;">
         <ul class="right-panel-tabs nav nav-pills">
-          <li data-bind="css: { 'active' : activeRightTab() === 'assistant' }, visible: assistantAvailable"><a href="#functions" data-bind="click: function() { activeRightTab('assistant'); }">${ _('Assistant') }</a></li>
-          <li data-bind="css: { 'active' : activeRightTab() === 'functions' }"><a href="#functions" data-bind="click: function() { activeRightTab('functions'); }">${ _('Functions') }</a></li>
-          <li data-bind="css: { 'active' : activeRightTab() === 'schedules' }"><a href="#functions" data-bind="click: function() { activeRightTab('schedules'); }">${ _('Schedules') }</a></li>
+          <li data-bind="css: { 'active' : activeRightTab() === 'assistant' }, visible: assistantAvailable"><a href="javascript: void(0);" data-bind="click: function() { activeRightTab('assistant'); }">${ _('Assistant') }</a></li>
+          <li data-bind="css: { 'active' : activeRightTab() === 'functions' }"><a href="javascript: void(0);" data-bind="click: function() { activeRightTab('functions'); }">${ _('Functions') }</a></li>
+          <li data-bind="css: { 'active' : activeRightTab() === 'schedules' }"><a href="javascript: void(0);" data-bind="click: function() { activeRightTab('schedules'); }">${ _('Schedules') }</a></li>
         </ul>
 
         <div class="right-panel-tab-content tab-content">
@@ -404,6 +404,24 @@ ${ hueIcons.symbols() }
         </div>
       </div>
     </div>
+
+    <div class="context-panel" data-bind="css: { 'visible': contextPanelVisible }">
+      <ul class="nav nav-tabs">
+        <!-- ko if: sessionsAvailable -->
+        <li class="active"><a href="#sessionsTab" data-toggle="tab" data-bind="visible: sessionsAvailable">${_('Sessions')}</a></li>
+        <!-- /ko -->
+      </ul>
+
+      <div class="tab-content">
+        <!-- ko if: sessionsAvailable -->
+        <div class="tab-pane active" id="sessionsTab">
+          <div class="row-fluid">
+            <div class="span12" data-bind="template: { name: 'notebook-session-config-template', data: activeAppViewModel }"></div>
+          </div>
+        </div>
+        <!-- /ko -->
+      </div>
+    </div>
   </div>
 </div>
 
@@ -571,15 +589,30 @@ ${ assist.assistPanel() }
 
         self.embeddable_cache = {};
 
-        self.getActiveAppViewmodel = function () {
-          var koElementID = '#' + self.currentApp() + 'Components';
-          if ($(koElementID).length > 0 && ko.dataFor($(koElementID)[0])) {
-            return ko.dataFor($(koElementID)[0]);
-          }
-          return null;
+        self.getActiveAppViewModel = function (callback) {
+          var checkInterval = window.setInterval(function () {
+            var $koElement = $('#' + self.currentApp() + 'Components');
+            if ($koElement.length > 0 && ko.dataFor($koElement[0])) {
+              window.clearInterval(checkInterval);
+              callback(ko.dataFor($koElement[0]));
+            }
+          }, 25);
         }
 
         self.currentApp = ko.observable();
+
+        self.currentApp.subscribe(function () {
+          self.getActiveAppViewModel(function (viewModel) {
+            huePubSub.publish('set.current.app.view.model', viewModel);
+          })
+        })
+
+        huePubSub.subscribe('get.current.app.view.model', function () {
+          self.getActiveAppViewModel(function (viewModel) {
+            huePubSub.publish('set.current.app.view.model', viewModel);
+          })
+        })
+
         self.isLoadingEmbeddable = ko.observable(false);
 
         self.extraEmbeddableURLParams = ko.observable('');
@@ -587,14 +620,13 @@ ${ assist.assistPanel() }
         self.changeEditorType = function (type) {
           self.extraEmbeddableURLParams('?type=' + type);
           hueUtils.changeURLParameter('type', type);
-          var checkForEditor = window.setInterval(function(){
-            if (self.getActiveAppViewmodel() && self.getActiveAppViewmodel().selectedNotebook && self.getActiveAppViewmodel().selectedNotebook()) {
-              self.getActiveAppViewmodel().selectedNotebook().selectedSnippet(type);
-              self.getActiveAppViewmodel().editorType(type);
-              self.getActiveAppViewmodel().newNotebook();
-              window.clearInterval(checkForEditor);
+          self.getActiveAppViewModel(function (viewModel) {
+            if (viewModel && viewModel.selectedNotebook && viewModel.selectedNotebook()) {
+              viewModel.selectedNotebook().selectedSnippet(type);
+              viewModel.editorType(type);
+              viewModel.newNotebook();
             }
-          }, 100);
+          })
         }
 
         huePubSub.subscribe('open.fb.file', function (path) {
@@ -758,6 +790,19 @@ ${ assist.assistPanel() }
         self.rightAssistVisible = ko.observable();
         self.assistantAvailable = ko.observable(false);
         self.activeRightTab = ko.observable();
+        self.activeAppViewModel = ko.observable();
+
+        self.contextPanelVisible = ko.observable(false);
+        self.sessionsAvailable = ko.observable(false);
+
+        self.activeAppViewModel.subscribe(function (viewModel) {
+          self.sessionsAvailable(typeof viewModel.selectedNotebook !== 'undefined');
+        })
+
+        huePubSub.subscribe('context.panel.visible', function (visible) {
+          console.log(visible);
+          self.contextPanelVisible(visible);
+        })
 
         huePubSub.subscribe('active.snippet.type.changed', function (type) {
           if (type === 'hive' || type === 'impala') {
@@ -773,12 +818,15 @@ ${ assist.assistPanel() }
           }
         });
 
+        huePubSub.subscribe('set.current.app.view.model', self.activeAppViewModel);
+        huePubSub.publish('get.current.app.view.model');
+
         if (!self.activeRightTab()) {
           self.activeRightTab('functions');
         }
 
         if (self.assistantAvailable()) {
-          self.activeRightTab = ko.observable('functions');
+          self.activeRightTab = ko.observable('assistant');
         }
 
         self.apiHelper.withTotalStorage('assist', 'left_assist_panel_visible', self.leftAssistVisible, true);
@@ -790,6 +838,7 @@ ${ assist.assistPanel() }
       ko.applyBindings(sidePanelViewModel, $('#leftResizer')[0]);
       ko.applyBindings(sidePanelViewModel, $('#rightResizer')[0]);
       ko.applyBindings(sidePanelViewModel, $('.right-panel')[0]);
+      ko.applyBindings(sidePanelViewModel, $('.context-panel')[0]);
       return sidePanelViewModel;
     })();
 

+ 3 - 0
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -2303,6 +2303,9 @@ var EditorViewModel = (function() {
     ApiHelper.getInstance(self).withTotalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
 
     self.isContextPanelVisible = ko.observable(false);
+    self.isContextPanelVisible.subscribe(function (newValue) {
+      huePubSub.publish('context.panel.visible', newValue);
+    });
 
     self.availableSnippets = ko.mapping.fromJS(options.languages);
 

+ 60 - 119
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -1079,140 +1079,81 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
     </ul>
 
     <div class="tab-content" style="border: none">
-      % if has_optimizer():
-      <div class="tab-pane active" id="assistantTab">
+      <div class="tab-pane ${ 'active' if not has_optimizer() else '' }" id="sessionsTab">
         <div class="row-fluid">
-          <div class="span12">
-            <form class="form-horizontal">
-              <fieldset>
-               ${ _('Active tables') }<br/>
-               <ul>
-                 <li>sample_07 <i class="fa fa-info"></i> <i class="fa fa-warning"></i> <i class="fa fa-refresh"></i></li>
-                 <li>sample_08 <i class="fa fa-info"></i> </li>
-               </ul>
-             </fieldset>
-
-            <form class="form-horizontal">
-              <fieldset>
-                ${ _('Fields') }<br/>
-                <ul>
-                  <li>'country-code' is a popular field <a href="javascript:void(0)">add</a></li>
-                  <li>'gender' would be a good dimension with low cardinality (2) <a href="javascript:void(0)">add</a></li>
-                  <li>'ts_s=17Q1' is the latest partition <a href="javascript:void(0)">add</a></li>
-                  <li>'f1'</li>
-                  <li>'f2'</li>
-                  <li>'f3'</li>
-                  <li>'f4'</li>
-                  <li>'f5'</li>
-                </ul>
-              </fieldset>
-            </form>
-
-            <form class="form-horizontal">
-              <fieldset>
-                ${ _('Suggestions') }<br/>
-                <ul>
-                  <li>Popular fields for the tables are: [code, salary, amount]</li>
-                  <li>The query would run 2x faster by adding a WHERE date_f > '2017-01-01'</li>
-                  <li>Parameterize the query?</li>
-                  <li>Could be automated with integrated scheduler</li>
-                  <li>Data has not been refreshed since last run 3 days ago  <i class="fa fa-warning"></i> <i class="fa fa-refresh"></i></li></li>
-                  <li>A schema change happened last week, a new column 'salary_med' was added</li>
-                  <li>Data statistics are not accurate, click to refresh them</li>
-                  <li>Query ran 17 times last week</li>
-                  <li>The datasets are sometimes joined with table [Population]</li>
-                  <li>Query would be a good candidate to run interactively with Impala</li>
-                </ul>
-              </fieldset>
-            </form>
-
-              <a href="javascript:void(0)" data-bind="click: function() { huePubSub.publish('editor.workload.upload'); }" title="${ _('Load past query history in order to improve recommendations') }">
-                <i class="fa fa-fw fa-cloud-upload"></i> ${_('Upload workload')}
-              </a>
+          <div class="span12" data-bind="template: { name: 'notebook-session-config-template', data: $root }"></div>
+        </div>
+      </div>
 
-           </form>
-         </div>
-       </div>
+      % if ENABLE_QUERY_SCHEDULING.get():
+      <!-- ko if: $root.selectedNotebook() && $root.selectedNotebook().isBatchable() -->
+      <!-- ko with: $root.selectedNotebook() -->
+      <div class="tab-pane" id="scheduleTab">
+        <!-- ko if: isSaved() -->
+        <!-- ko if: schedulerViewModelIsLoaded() && schedulerViewModel.coordinator.isDirty() -->
+        <a data-bind="click: $root.saveNotebook">${ _('Changes not saved') }</a>
+        <!-- /ko -->
+        <!-- ko if: schedulerViewModelIsLoaded() && ! schedulerViewModel.coordinator.isDirty() -->
+        <a data-bind="click: showSubmitPopup">${ _('Start') }</a>
+        <!-- /ko -->
+        <br>
+        <br>
+        <div id="schedulerEditor"></div>
+        <!-- /ko -->
+        <!-- ko ifnot: isSaved() -->
+        ${ _('Query needs to be saved first.') }
+        <!-- /ko -->
       </div>
+      <!-- /ko -->
+      <!-- /ko -->
       % endif
+    </div>
+  </div>
+</script>
 
-      <div class="tab-pane ${ 'active' if not has_optimizer() else '' }" id="sessionsTab">
-        <div class="row-fluid">
-          <div class="span12">
-            <!-- ko with: $root.selectedNotebook() -->
-            <form class="form-horizontal session-config">
-              <fieldset>
-                <!-- ko ifnot: sessions().length -->
-               <p>${ _('There are currently no active sessions, please reload the page.') }</p>
-               <!-- /ko -->
-               <!-- ko foreach: sessions -->
-               <h4 data-bind="text: $root.getSnippetName(type())" style="clear:left; display: inline-block"></h4>
-               <div class="session-actions">
-                 <a class="inactive-action pointer" title="${ _('Recreate session') }" rel="tooltip" data-bind="click: function() { $root.selectedNotebook().restartSession($data) }"><i class="fa fa-refresh" data-bind="css: { 'fa-spin': restarting }"></i> ${ _('Recreate') }</a>
-                 <a class="inactive-action pointer margin-left-10" title="${ _('Close session') }" rel="tooltip" data-bind="click: function() { $root.selectedNotebook().closeAndRemoveSession($data) }"><i class="fa fa-times"></i> ${ _('Close') }</a>
-                 % if conf.USE_DEFAULT_CONFIGURATION.get():
-                 <a class="inactive-action pointer margin-left-10" title="${ _('Save session settings as default') }" rel="tooltip" data-bind="click: function() { $root.selectedNotebook().saveDefaultUserProperties($data) }"><i class="fa fa-save"></i> ${ _('Set as default settings') }</a>
-                 % endif
-                 <!-- ko if: type()== 'impala' && typeof http_addr != 'undefined' -->
-                 <a class="margin-left-10" data-bind="attr: {'href': window.location.protocol + '//' + http_addr().replace(/^(https?):\/\//, '')}" target="_blank"><i class="fa fa-external-link"></i> <span data-bind="text: http_addr().replace(/^(https?):\/\//, '')"></span></a>
-                 <!-- /ko -->
-               </div>
-               <div style="width:100%;">
-                 <!-- ko component: { name: 'property-selector', params: { properties: properties } } --><!-- /ko -->
-               </div>
-               <div style="clear:both; padding-left: 120px;">
-                 <!-- ko if: availableNewProperties().length -->
-                 <select data-bind="options: availableNewProperties,
+<script type="text/html" id="notebook-session-config-template">
+  <!-- ko with: selectedNotebook() -->
+  <form class="form-horizontal session-config">
+    <fieldset>
+      <!-- ko ifnot: sessions().length -->
+      <p>${ _('There are currently no active sessions, please reload the page.') }</p>
+      <!-- /ko -->
+      <!-- ko foreach: sessions -->
+      <h4 data-bind="text: $parents[1].getSnippetName(type())" style="clear:left; display: inline-block"></h4>
+      <div class="session-actions">
+        <a class="inactive-action pointer" title="${ _('Recreate session') }" rel="tooltip" data-bind="click: function() { $parent.restartSession($data) }"><i class="fa fa-refresh" data-bind="css: { 'fa-spin': restarting }"></i> ${ _('Recreate') }</a>
+        <a class="inactive-action pointer margin-left-10" title="${ _('Close session') }" rel="tooltip" data-bind="click: function() { $parent.closeAndRemoveSession($data) }"><i class="fa fa-times"></i> ${ _('Close') }</a>
+        % if conf.USE_DEFAULT_CONFIGURATION.get():
+          <a class="inactive-action pointer margin-left-10" title="${ _('Save session settings as default') }" rel="tooltip" data-bind="click: function() { $parent.saveDefaultUserProperties($data) }"><i class="fa fa-save"></i> ${ _('Set as default settings') }</a>
+        % endif
+        <!-- ko if: type()== 'impala' && typeof http_addr != 'undefined' -->
+        <a class="margin-left-10" data-bind="attr: {'href': window.location.protocol + '//' + http_addr().replace(/^(https?):\/\//, '')}" target="_blank"><i class="fa fa-external-link"></i> <span data-bind="text: http_addr().replace(/^(https?):\/\//, '')"></span></a>
+        <!-- /ko -->
+      </div>
+      <div style="width:100%;">
+        <!-- ko component: { name: 'property-selector', params: { properties: properties } } --><!-- /ko -->
+      </div>
+      <div style="clear:both; padding-left: 120px;">
+        <!-- ko if: availableNewProperties().length -->
+        <select data-bind="options: availableNewProperties,
                           optionsText: 'nice_name',
                           optionsValue: 'name',
                           value: selectedSessionProperty,
                           optionsCaption: '${ _ko('Choose a property...') }'"></select>
-                 <a class="pointer" style="padding:5px;" data-bind="click: selectedSessionProperty() && function() {
+        <a class="pointer" style="padding:5px;" data-bind="click: selectedSessionProperty() && function() {
                     properties.push(ko.mapping.fromJS({'name': selectedSessionProperty(), 'value': ''}));
                     selectedSessionProperty('');
                    }" style="margin-left:10px;vertical-align: text-top;">
-                   <i class="fa fa-plus"></i>
-                 </a>
-                 <!-- /ko -->
-               </div>
-               <!-- /ko -->
-               <!-- /ko -->
-               <br/>
-             </fieldset>
-           </form>
-           <!-- /ko -->
-         </div>
-       </div>
-    </div>
-
-    % if ENABLE_QUERY_SCHEDULING.get():
-    <!-- ko if: $root.selectedNotebook() && $root.selectedNotebook().isBatchable() -->
-    <!-- ko with: $root.selectedNotebook() -->
-    <div class="tab-pane" id="scheduleTab">
-      <!-- ko if: isSaved() -->
-        <!-- ko if: schedulerViewModelIsLoaded() && schedulerViewModel.coordinator.isDirty() -->
-          <a data-bind="click: $root.saveNotebook">${ _('Changes not saved') }</a>
-        <!-- /ko -->
-        <!-- ko if: schedulerViewModelIsLoaded() && ! schedulerViewModel.coordinator.isDirty() -->
-          <a data-bind="click: showSubmitPopup">${ _('Start') }</a>
+          <i class="fa fa-plus"></i>
+        </a>
         <!-- /ko -->
-        <br>
-        <br>
-        <div id="schedulerEditor">
-        </div>
+      </div>
       <!-- /ko -->
-
-      <!-- ko ifnot: isSaved() -->
-        ${ _('Query needs to be saved first.') }
       <!-- /ko -->
-    </div>
-    <!-- /ko -->
-    <!-- /ko -->
-
-    <!-- /ko -->
-    % endif
-
-  </div>
+      <br/>
+    </fieldset>
+  </form>
+  <!-- /ko -->
 </script>
 
 <script type="text/html" id="snippetIcon">