Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Select from Project' wizard to allow project templatesto be imported #1966

Merged
merged 1 commit into from
Sep 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/scripts/controllers/landingPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ angular.module('openshiftConsole')
_.set($scope, 'ordering.panelName', 'fromFile');
};

$scope.fromProjectSelected = function() {
_.set($scope, 'ordering.panelName', 'fromProject');
};

AuthService.withUser().then(function() {
var includeTemplates = !_.get(Constants, 'ENABLE_TECH_PREVIEW_FEATURE.template_service_broker');
Catalog.getCatalogItems(includeTemplates).then(_.spread(function(items, errorMessage) {
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/directives/processTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
bindings: {
template: '<',
project: '<',
onProjectSelected: '<',
availableProjects: '<',
prefillParameters: '<',
isDialog: '<'
},
Expand Down
168 changes: 165 additions & 3 deletions app/scripts/directives/processTemplateDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,46 @@
angular.module('openshiftConsole').component('processTemplateDialog', {
controller: [
'$scope',
'$filter',
'Catalog',
'DataService',
'KeywordService',
'NotificationsService',
'ProjectsService',
'RecentlyViewedProjectsService',
ProcessTemplateDialog
],
controllerAs: '$ctrl',
bindings: {
template: '<',
project: '<',
useProjectTemplate: '<',
onDialogClosed: '&'
},
templateUrl: 'views/directives/process-template-dialog.html'
});

function ProcessTemplateDialog($scope, DataService) {
function ProcessTemplateDialog($scope,
$filter,
Catalog,
DataService,
KeywordService,
NotificationsService,
ProjectsService,
RecentlyViewedProjectsService) {
var ctrl = this;
var validityWatcher;

ctrl.selectStep = {
id: 'projectTemplates',
label: 'Selection',
view: 'views/directives/process-template-dialog/process-template-select.html',
hidden: ctrl.useProjectTemplate !== true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctrl.useProjectTemplate !== true vs !ctrl.useProjectTemplate? Non-boolean options possible?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a passed in parameter, I am defaulting to false unless explicitly set to true.

allowed: true,
valid: false,
onShow: showSelect
};

ctrl.configStep = {
id: 'configuration',
label: 'Configuration',
Expand All @@ -43,6 +68,38 @@

ctrl.$onInit = function() {
ctrl.loginBaseUrl = DataService.openshiftAPIBaseUrl();
ctrl.preSelectedProject = ctrl.selectedProject = ctrl.project;
listProjects();

ctrl.projectEmptyState = {
icon: 'pficon pficon-info',
title: 'No Project Selected',
info: 'Please select a project from the dropdown to load Templates from that project.'
};

ctrl.templatesEmptyState = {
icon: 'pficon pficon-info',
title: 'No Templates',
info: 'The selected project has no templates available to import.'
};

ctrl.filterConfig = {
fields: [
{
id: 'keyword',
title: 'Keyword',
placeholder: 'Filter by Keyword',
filterType: 'text'
}
],
inlineResults: true,
showTotalCountResults: true,
itemsLabel: 'Item',
itemsLabelPlural: 'Items',
resultsCount: 0,
appliedFilters: [],
onFilterChange: filterChange
};
};

ctrl.$onChanges = function(changes) {
Expand All @@ -52,6 +109,9 @@
ctrl.iconClass = getIconClass();
}
}
if (changes.useProjectTemplate) {
initializeSteps();
}
};

$scope.$on('templateInstantiated', function(event, message) {
Expand Down Expand Up @@ -85,13 +145,49 @@
}
};

ctrl.onProjectSelected = function(project) {
ctrl.selectedProject = project;
ctrl.configStep.valid = $scope.$ctrl.form.$valid && ctrl.selectedProject;
};

ctrl.templateSelected = function(template) {
ctrl.selectedTemplate = template;
ctrl.template = _.get(template, 'resource');
ctrl.selectStep.valid = !!template;
};

ctrl.templateProjectChange = function () {
ctrl.templateProjectName = _.get(ctrl.templateProject, 'metadata.name');

// Get the templates for the selected project
ctrl.catalogItems = {};
ctrl.templateSelected();

Catalog.getProjectCatalogItems(ctrl.templateProjectName, false, true).then( _.spread(function(catalogServiceItems, errorMessage) {
ctrl.catalogItems = catalogServiceItems;
ctrl.totalCount = ctrl.catalogItems.length;
filterItems();

if (errorMessage) {
NotificationsService.addNotification(
{
type: "error",
message: errorMessage
}
);
}
}));
};

function getIconClass() {
var icon = _.get(ctrl, 'template.metadata.annotations.iconClass', 'fa fa-clone');
return (icon.indexOf('icon-') !== -1) ? 'font-icon ' + icon : icon;
}

function initializeSteps() {
ctrl.steps = [ctrl.configStep, ctrl.resultsStep];
if (!ctrl.steps) {
ctrl.steps = [ctrl.selectStep, ctrl.configStep, ctrl.resultsStep];
}
}

function clearValidityWatcher() {
Expand All @@ -101,19 +197,30 @@
}
}

function showSelect() {
ctrl.selectStep.selected = true;
ctrl.configStep.selected = false;
ctrl.resultsStep.selected = false;
ctrl.nextTitle = "Next >";
clearValidityWatcher();
listProjects();
}

function showConfig() {
ctrl.selectStep.selected = false;
ctrl.configStep.selected = true;
ctrl.resultsStep.selected = false;
ctrl.nextTitle = "Create";
ctrl.resultsStep.allowed = ctrl.configStep.valid;

validityWatcher = $scope.$watch("$ctrl.form.$valid", function(isValid) {
ctrl.configStep.valid = isValid;
ctrl.configStep.valid = isValid && ctrl.selectedProject;
ctrl.resultsStep.allowed = isValid;
});
}

function showResults() {
ctrl.selectStep.selected = false;
ctrl.configStep.selected = false;
ctrl.resultsStep.selected = true;
ctrl.nextTitle = "Close";
Expand All @@ -124,5 +231,60 @@
function instantiateTemplate() {
$scope.$broadcast('instantiateTemplate');
}

function filterForKeywords(searchText, items) {
return KeywordService.filterForKeywords(items, ['name', 'tags'], KeywordService.generateKeywords(searchText));
}

function filterChange(filters) {
ctrl.filterConfig.appliedFilters = filters;
filterItems();
}

function filterItems() {
ctrl.filteredItems = ctrl.catalogItems;
if (ctrl.filterConfig.appliedFilters && ctrl.filterConfig.appliedFilters.length > 0) {
_.each(ctrl.filterConfig.appliedFilters, function(filter) {
ctrl.filteredItems = filterForKeywords(filter.value, ctrl.filteredItems);
});
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if order of flow here matters, since what is inside updateFilterControls() is wrapped in $timeout...ie, could this call be the last line in this fn since its contexts will execute later?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted, not sure what you were getting at but the timeout was unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! I just have a tendency to pause and ask a question when I see $timeout, they are good at hiding bugs. Wasn't sure about your code here, but figured I'd mention just in case.

// Deselect the currently selected template if it was filtered out
if (!_.includes(ctrl.filteredItems, ctrl.selectedTemplate)) {
ctrl.templateSelected();
}

updateFilterControls();
}

function updateFilterControls() {
ctrl.filterConfig.resultsCount = ctrl.filteredItems.length;

if (ctrl.totalCount <= 1) {
$('.filter-pf.filter-fields input').attr('disabled', '');
} else {
$('.filter-pf.filter-fields input').removeAttr("disabled");
}
}

var updateProjects = function() {
var filteredProjects = _.reject(ctrl.unfilteredProjects, 'metadata.deletionTimestamp');
var projects = _.sortBy(filteredProjects, $filter('displayName'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, this _.sortBy(...) is unnecessary as RecentlyViewedProjectsService.orderByMostRecentlyViewed(projects) handles a case insensitive sort of displayName.

ctrl.searchEnabled = !_.isEmpty(filteredProjects);

ctrl.templateProjects = RecentlyViewedProjectsService.orderByMostRecentlyViewed(projects);
};

function listProjects() {
if (!ctrl.unfilteredProjects) {
ProjectsService.list().then(function(projectData) {
ctrl.unfilteredProjects = _.toArray(projectData.by("metadata.name"));
}, function() {
ctrl.unfilteredProjects = [];
}).finally(function() {
updateProjects();
});
}
}
}
})();
48 changes: 48 additions & 0 deletions app/styles/_core.less
Original file line number Diff line number Diff line change
Expand Up @@ -1246,3 +1246,51 @@ pre.clipped {
width: 30px;
}
}

.order-service-config {
.order-services-filter {
margin-left: 0;
}
.blank-slate-pf {
margin-bottom: 0;
padding-bottom: 0;
}
.select-project-for-template {
border-bottom: solid 1px @color-pf-black-300;
padding-bottom: 10px;

> h2 {
margin-bottom: 20px;
margin-top: 0;
}
.ui-select-container {
display: inline-block;
width: 275px;
}
}
.services-item {
&.show-selection {
// Clear focus settings, keep before active settings
&:focus {
color: @text-color;
.services-item-icon:after {
border: none;
}

.services-item-name {
color: @text-color;
}
}
&.active {
color: @link-hover-color;
.services-item-icon:after {
border: 2px solid @link-color;
}
.services-item-name {
color: @link-hover-color;
}
}
}
}
}

3 changes: 3 additions & 0 deletions app/styles/_overlay-forms.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.order-service-config-single-column {
width: 100%;
@media (min-width: 768px) {
padding-left: 0;
}
}

.wizard-pf-main {
Expand Down
2 changes: 2 additions & 0 deletions app/views/directives/header/project-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<li ng-if-start="catalogLandingPageEnabled" role="menuitem"><a href="/">Browse Catalog</a></li>
<li role="menuitem"><a href="" ng-click="showOrderingPanel('deployImage')">Deploy Image</a></li>
<li ng-if-end role="menuitem"><a href="" ng-click="showOrderingPanel('fromFile')">Import YAML / JSON</a></li>
<li ng-if-end role="menuitem"><a href="" ng-click="showOrderingPanel('fromProject')">Select from Project</a></li>
</ul>
</div>
<div row
Expand All @@ -51,4 +52,5 @@
<overlay-panel show-panel="ordering.panelName" show-close="true" handle-close="closeOrderingPanel">
<deploy-image-dialog ng-if="ordering.panelName === 'deployImage'" project="project" context="context" on-dialog-closed="closeOrderingPanel"></deploy-image-dialog>
<from-file-dialog ng-if="ordering.panelName === 'fromFile'" project="project" context="context" on-dialog-closed="closeOrderingPanel"></from-file-dialog>
<process-template-dialog ng-if="ordering.panelName === 'fromProject'" project="project" use-project-template="true" on-dialog-closed="closeOrderingPanel"></process-template-dialog>
</overlay-panel>
8 changes: 4 additions & 4 deletions app/views/directives/process-template-dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<pf-wizard
hide-header="true"
hide-sidebar="true"
hide-back-button="true"
hide-back-button="!$ctrl.useProjectTemplate"
step-class="order-service-wizard-step"
wizard-ready="$ctrl.wizardReady"
next-title="$ctrl.nextTitle"
Expand All @@ -11,7 +11,7 @@
on-cancel="$ctrl.close()"
wizard-done="$ctrl.wizardDone"
current-step="$ctrl.currentStep"
class="pf-wizard-no-back">
ng-class="{'pf-wizard-no-back': !$ctrl.useProjectTemplate}">
<pf-wizard-step ng-repeat="step in $ctrl.steps track by step.id"
step-title="{{step.label}}"
wz-disabled="{{step.hidden}}"
Expand All @@ -22,7 +22,7 @@
step-id="{{step.id}}"
step-priority="{{$index}}">
<div class="wizard-pf-main-inner-shadow-covers">
<div class="order-service-details">
<div class="order-service-details" ng-if="!$ctrl.selectStep.selected">
<div class="order-service-details-top">
<div class="service-icon">
<span class="icon {{$ctrl.iconClass}}"></span>
Expand All @@ -42,7 +42,7 @@
<p ng-bind-html="$ctrl.template | description | linky : '_blank'" class="description"></p>
</div>
</div>
<div class="order-service-config">
<div class="order-service-config" ng-class="{'order-service-config-single-column': $ctrl.selectStep.selected}">
<div ng-if="step.selected" ng-include="step.view" class="wizard-pf-main-form-contents"></div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="osc-form">
<form name="$ctrl.form">
<process-template template="$ctrl.template" is-dialog="true"></process-template>
<process-template template="$ctrl.template" project="$ctrl.preSelectedProject" on-project-selected="$ctrl.onProjectSelected" available-projects="$ctrl.unfilteredProjects" is-dialog="true"></process-template>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<next-steps
project="$ctrl.selectedProject"
project-name="$ctrl.selectedProject.metadata.name"
login-base-url="$ctrl.loginBaseUrl">
login-base-url="$ctrl.loginBaseUrl"
on-continue="$ctrl.close">
</next-steps>
Loading