Skip to content

Commit 67ab911

Browse files
Add 'Select from Project' wizard to allow project templates/images to be imported
1 parent c9a5d1a commit 67ab911

16 files changed

+553
-120
lines changed

app/scripts/controllers/landingPage.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ angular.module('openshiftConsole')
8585
_.set($scope, 'ordering.panelName', 'fromFile');
8686
};
8787

88+
$scope.fromProjectSelected = function() {
89+
_.set($scope, 'ordering.panelName', 'fromProject');
90+
};
91+
8892
AuthService.withUser().then(function() {
8993
var includeTemplates = !_.get(Constants, 'ENABLE_TECH_PREVIEW_FEATURE.template_service_broker');
9094
Catalog.getCatalogItems(includeTemplates).then(_.spread(function(items, errorMessage) {

app/scripts/directives/processTemplate.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
bindings: {
2323
template: '<',
2424
project: '<',
25+
onProjectSelected: '<',
26+
availableProjects: '<',
2527
prefillParameters: '<',
2628
isDialog: '<'
2729
},

app/scripts/directives/processTemplateDialog.js

Lines changed: 171 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,46 @@
44
angular.module('openshiftConsole').component('processTemplateDialog', {
55
controller: [
66
'$scope',
7+
'$filter',
8+
'Catalog',
79
'DataService',
10+
'KeywordService',
11+
'NotificationsService',
12+
'ProjectsService',
13+
'RecentlyViewedProjectsService',
814
ProcessTemplateDialog
915
],
1016
controllerAs: '$ctrl',
1117
bindings: {
1218
template: '<',
19+
project: '<',
20+
useProjectTemplate: '<',
1321
onDialogClosed: '&'
1422
},
1523
templateUrl: 'views/directives/process-template-dialog.html'
1624
});
1725

18-
function ProcessTemplateDialog($scope, DataService) {
26+
function ProcessTemplateDialog($scope,
27+
$filter,
28+
Catalog,
29+
DataService,
30+
KeywordService,
31+
NotificationsService,
32+
ProjectsService,
33+
RecentlyViewedProjectsService) {
1934
var ctrl = this;
2035
var validityWatcher;
2136

37+
ctrl.selectStep = {
38+
id: 'projectTemplates',
39+
label: 'Selection',
40+
view: 'views/directives/process-template-dialog/process-template-select.html',
41+
hidden: ctrl.useProjectTemplate !== true,
42+
allowed: true,
43+
valid: false,
44+
onShow: showSelect
45+
};
46+
2247
ctrl.configStep = {
2348
id: 'configuration',
2449
label: 'Configuration',
@@ -43,6 +68,34 @@
4368

4469
ctrl.$onInit = function() {
4570
ctrl.loginBaseUrl = DataService.openshiftAPIBaseUrl();
71+
ctrl.preSelectedProject = ctrl.selectedProject = ctrl.project;
72+
listProjects();
73+
74+
ctrl.projectEmptyState = {
75+
icon: 'pficon pficon-info',
76+
title: 'No Project Selected',
77+
info: 'Please select a project from the dropdown to load Templates and Images from that project.'
78+
};
79+
80+
ctrl.templatesEmptyState = {
81+
icon: 'pficon pficon-info',
82+
title: 'No Templates or Images',
83+
info: 'The selected project has no templates or images available to import.'
84+
};
85+
86+
ctrl.filterConfig = {
87+
fields: [
88+
{
89+
id: 'keyword',
90+
title: 'Keyword',
91+
placeholder: 'Filter by Keyword',
92+
filterType: 'text'
93+
}
94+
],
95+
resultsCount: 0,
96+
appliedFilters: [],
97+
onFilterChange: filterChange
98+
};
4699
};
47100

48101
ctrl.$onChanges = function(changes) {
@@ -52,6 +105,9 @@
52105
ctrl.iconClass = getIconClass();
53106
}
54107
}
108+
if (changes.useProjectTemplate) {
109+
initializeSteps();
110+
}
55111
};
56112

57113
$scope.$on('templateInstantiated', function(event, message) {
@@ -85,13 +141,49 @@
85141
}
86142
};
87143

144+
ctrl.onProjectSelected = function(project) {
145+
ctrl.selectedProject = project;
146+
ctrl.configStep.valid = $scope.$ctrl.form.$valid && ctrl.selectedProject;
147+
};
148+
149+
ctrl.templateSelected = function(template) {
150+
ctrl.selectedTemplate = template;
151+
ctrl.template = _.get(template, 'resource');
152+
ctrl.selectStep.valid = !!template;
153+
};
154+
155+
ctrl.templateProjectChange = function () {
156+
ctrl.templateProjectName = _.get(ctrl.templateProject, 'metadata.name');
157+
158+
// Get the templates for the selected project
159+
ctrl.catalogItems = {};
160+
ctrl.templateSelected();
161+
162+
Catalog.getProjectCatalogItems(ctrl.templateProjectName, false, true).then( _.spread(function(catalogServiceItems, errorMessage) {
163+
ctrl.catalogItems = catalogServiceItems;
164+
ctrl.totalCount = ctrl.catalogItems.length;
165+
filterItems();
166+
167+
if (errorMessage) {
168+
NotificationsService.addNotification(
169+
{
170+
type: "error",
171+
message: errorMessage
172+
}
173+
);
174+
}
175+
}));
176+
};
177+
88178
function getIconClass() {
89179
var icon = _.get(ctrl, 'template.metadata.annotations.iconClass', 'fa fa-clone');
90180
return (icon.indexOf('icon-') !== -1) ? 'font-icon ' + icon : icon;
91181
}
92182

93183
function initializeSteps() {
94-
ctrl.steps = [ctrl.configStep, ctrl.resultsStep];
184+
if (!ctrl.steps) {
185+
ctrl.steps = [ctrl.selectStep, ctrl.configStep, ctrl.resultsStep];
186+
}
95187
}
96188

97189
function clearValidityWatcher() {
@@ -101,19 +193,30 @@
101193
}
102194
}
103195

196+
function showSelect() {
197+
ctrl.selectStep.selected = true;
198+
ctrl.configStep.selected = false;
199+
ctrl.resultsStep.selected = false;
200+
ctrl.nextTitle = "Next >";
201+
clearValidityWatcher();
202+
listProjects();
203+
}
204+
104205
function showConfig() {
206+
ctrl.selectStep.selected = false;
105207
ctrl.configStep.selected = true;
106208
ctrl.resultsStep.selected = false;
107209
ctrl.nextTitle = "Create";
108210
ctrl.resultsStep.allowed = ctrl.configStep.valid;
109211

110212
validityWatcher = $scope.$watch("$ctrl.form.$valid", function(isValid) {
111-
ctrl.configStep.valid = isValid;
213+
ctrl.configStep.valid = isValid && ctrl.selectedProject;
112214
ctrl.resultsStep.allowed = isValid;
113215
});
114216
}
115217

116218
function showResults() {
219+
ctrl.selectStep.selected = false;
117220
ctrl.configStep.selected = false;
118221
ctrl.resultsStep.selected = true;
119222
ctrl.nextTitle = "Close";
@@ -124,5 +227,70 @@
124227
function instantiateTemplate() {
125228
$scope.$broadcast('instantiateTemplate');
126229
}
230+
231+
function filterForKeywords(searchText, items) {
232+
return KeywordService.filterForKeywords(items, ['name', 'tags'], KeywordService.generateKeywords(searchText));
233+
}
234+
235+
function filterChange(filters) {
236+
// only use filters which have a filter criteria 'value'
237+
// prevents applying an empty keyword filter
238+
// TODO: can remove the following line of code after angular-patternfly issue is fixed:
239+
// https://github.com/patternfly/angular-patternfly/issues/509
240+
filters = _.filter(filters, 'value');
241+
242+
ctrl.filterConfig.appliedFilters = filters;
243+
filterItems();
244+
}
245+
246+
function filterItems() {
247+
ctrl.filteredItems = ctrl.catalogItems;
248+
if (ctrl.filterConfig.appliedFilters && ctrl.filterConfig.appliedFilters.length > 0) {
249+
_.each(ctrl.filterConfig.appliedFilters, function(filter) {
250+
ctrl.filteredItems = filterForKeywords(filter.value, ctrl.filteredItems);
251+
});
252+
}
253+
254+
// Deselect the currently selected template if it was filtered out
255+
if (!_.includes(ctrl.filteredItems, ctrl.selectedTemplate)) {
256+
ctrl.templateSelected();
257+
}
258+
259+
updateFilterControls();
260+
}
261+
262+
function updateFilterControls() {
263+
if (ctrl.filterConfig.appliedFilters.length > 0) {
264+
ctrl.filterConfig.resultsCount = ctrl.filteredItems.length;
265+
$('.toolbar-pf-results h5').text(ctrl.filterConfig.resultsCount + ' of ' + ctrl.totalCount + ' items');
266+
} else {
267+
$('.toolbar-pf-results h5').text(ctrl.totalCount + (ctrl.totalCount === 1 ? ' item' : ' items'));
268+
if (ctrl.totalCount <= 1) {
269+
$('.filter-pf.filter-fields input').attr('disabled', '');
270+
} else {
271+
$('.filter-pf.filter-fields input').removeAttr("disabled");
272+
}
273+
}
274+
}
275+
276+
var updateProjects = function() {
277+
var filteredProjects = _.reject(ctrl.unfilteredProjects, 'metadata.deletionTimestamp');
278+
var projects = _.sortBy(filteredProjects, $filter('displayName'));
279+
ctrl.searchEnabled = !_.isEmpty(filteredProjects);
280+
281+
ctrl.templateProjects = RecentlyViewedProjectsService.orderByMostRecentlyViewed(projects);
282+
};
283+
284+
function listProjects() {
285+
if (!ctrl.unfilteredProjects) {
286+
ProjectsService.list().then(function(projectData) {
287+
ctrl.unfilteredProjects = _.toArray(projectData.by("metadata.name"));
288+
}, function() {
289+
ctrl.unfilteredProjects = [];
290+
}).finally(function() {
291+
updateProjects();
292+
});
293+
}
294+
}
127295
}
128296
})();

app/styles/_core.less

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,3 +1259,51 @@ pre.clipped {
12591259
width: 30px;
12601260
}
12611261
}
1262+
1263+
.order-service-config {
1264+
.order-services-filter {
1265+
margin-left: 0;
1266+
}
1267+
.blank-slate-pf {
1268+
margin-bottom: 0;
1269+
padding-bottom: 0;
1270+
}
1271+
.select-project-for-template {
1272+
border-bottom: solid 1px @color-pf-black-300;
1273+
padding-bottom: 10px;
1274+
1275+
> h2 {
1276+
margin-bottom: 20px;
1277+
margin-top: 0;
1278+
}
1279+
.ui-select-container {
1280+
display: inline-block;
1281+
width: 275px;
1282+
}
1283+
}
1284+
.services-item {
1285+
&.show-selection {
1286+
// Clear focus settings, keep before active settings
1287+
&:focus {
1288+
color: @text-color;
1289+
.services-item-icon:after {
1290+
border: none;
1291+
}
1292+
1293+
.services-item-name {
1294+
color: @text-color;
1295+
}
1296+
}
1297+
&.active {
1298+
color: @link-hover-color;
1299+
.services-item-icon:after {
1300+
border: 2px solid @link-color;
1301+
}
1302+
.services-item-name {
1303+
color: @link-hover-color;
1304+
}
1305+
}
1306+
}
1307+
}
1308+
}
1309+

app/styles/_overlay-forms.less

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
.order-service-config-single-column {
22
width: 100%;
3+
@media (min-width: 768px) {
4+
padding-left: 0;
5+
}
36
}
47

58
.wizard-pf-main {

app/views/directives/header/project-header.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<li ng-if-start="catalogLandingPageEnabled" role="menuitem"><a href="/">Browse Catalog</a></li>
3939
<li role="menuitem"><a href="" ng-click="showOrderingPanel('deployImage')">Deploy Image</a></li>
4040
<li ng-if-end role="menuitem"><a href="" ng-click="showOrderingPanel('fromFile')">Import YAML / JSON</a></li>
41+
<li ng-if-end role="menuitem"><a href="" ng-click="showOrderingPanel('fromProject')">Select from Project</a></li>
4142
</ul>
4243
</div>
4344
<div row
@@ -51,4 +52,5 @@
5152
<overlay-panel show-panel="ordering.panelName" show-close="true" handle-close="closeOrderingPanel">
5253
<deploy-image-dialog ng-if="ordering.panelName === 'deployImage'" project="project" context="context" on-dialog-closed="closeOrderingPanel"></deploy-image-dialog>
5354
<from-file-dialog ng-if="ordering.panelName === 'fromFile'" project="project" context="context" on-dialog-closed="closeOrderingPanel"></from-file-dialog>
55+
<process-template-dialog ng-if="ordering.panelName === 'fromProject'" project="project" use-project-template="true" on-dialog-closed="closeOrderingPanel"></process-template-dialog>
5456
</overlay-panel>

app/views/directives/process-template-dialog.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<pf-wizard
33
hide-header="true"
44
hide-sidebar="true"
5-
hide-back-button="true"
5+
hide-back-button="!$ctrl.useProjectTemplate"
66
step-class="order-service-wizard-step"
77
wizard-ready="$ctrl.wizardReady"
88
next-title="$ctrl.nextTitle"
@@ -11,7 +11,7 @@
1111
on-cancel="$ctrl.close()"
1212
wizard-done="$ctrl.wizardDone"
1313
current-step="$ctrl.currentStep"
14-
class="pf-wizard-no-back">
14+
ng-class="{'pf-wizard-no-back': !$ctrl.useProjectTemplate}">
1515
<pf-wizard-step ng-repeat="step in $ctrl.steps track by step.id"
1616
step-title="{{step.label}}"
1717
wz-disabled="{{step.hidden}}"
@@ -22,7 +22,7 @@
2222
step-id="{{step.id}}"
2323
step-priority="{{$index}}">
2424
<div class="wizard-pf-main-inner-shadow-covers">
25-
<div class="order-service-details">
25+
<div class="order-service-details" ng-if="!$ctrl.selectStep.selected">
2626
<div class="order-service-details-top">
2727
<div class="service-icon">
2828
<span class="icon {{$ctrl.iconClass}}"></span>
@@ -42,7 +42,7 @@
4242
<p ng-bind-html="$ctrl.template | description | linky : '_blank'" class="description"></p>
4343
</div>
4444
</div>
45-
<div class="order-service-config">
45+
<div class="order-service-config" ng-class="{'order-service-config-single-column': $ctrl.selectStep.selected}">
4646
<div ng-if="step.selected" ng-include="step.view" class="wizard-pf-main-form-contents"></div>
4747
</div>
4848
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="osc-form">
22
<form name="$ctrl.form">
3-
<process-template template="$ctrl.template" is-dialog="true"></process-template>
3+
<process-template template="$ctrl.template" project="$ctrl.preSelectedProject" on-project-selected="$ctrl.onProjectSelected" available-projects="$ctrl.unfilteredProjects" is-dialog="true"></process-template>
44
</form>
55
</div>
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<next-steps
22
project="$ctrl.selectedProject"
33
project-name="$ctrl.selectedProject.metadata.name"
4-
login-base-url="$ctrl.loginBaseUrl">
4+
login-base-url="$ctrl.loginBaseUrl"
5+
on-continue="$ctrl.close">
56
</next-steps>

0 commit comments

Comments
 (0)