diff --git a/awx/ui/client/features/credentials/add-credentials.controller.js b/awx/ui/client/features/credentials/add-credentials.controller.js
index 0dc60b46e5..b9d2c5ee7a 100644
--- a/awx/ui/client/features/credentials/add-credentials.controller.js
+++ b/awx/ui/client/features/credentials/add-credentials.controller.js
@@ -29,8 +29,8 @@ function AddCredentialsController (models, $state) {
return credential.request('post', data);
};
- vm.form.saveSuccess = res => {
- $state.go('credentials.edit', { id: res.data.id });
+ vm.form.onSaveSuccess = res => {
+ $state.go('credentials.edit', { credential: res.data });
};
}
diff --git a/awx/ui/client/features/credentials/add-credentials.view.html b/awx/ui/client/features/credentials/add-edit-credentials.view.html
similarity index 94%
rename from awx/ui/client/features/credentials/add-credentials.view.html
rename to awx/ui/client/features/credentials/add-edit-credentials.view.html
index 284c5f44ad..3e62f42611 100644
--- a/awx/ui/client/features/credentials/add-credentials.view.html
+++ b/awx/ui/client/features/credentials/add-edit-credentials.view.html
@@ -4,7 +4,6 @@
Details
Permissions
- Notifications
diff --git a/awx/ui/client/features/credentials/edit-credentials.controller.js b/awx/ui/client/features/credentials/edit-credentials.controller.js
new file mode 100644
index 0000000000..0887669f5d
--- /dev/null
+++ b/awx/ui/client/features/credentials/edit-credentials.controller.js
@@ -0,0 +1,44 @@
+function EditCredentialsController (models, $state) {
+ let vm = this || {};
+
+ let me = models.me;
+ let credential = models.credential;
+ let credentialType = models.credentialType;
+ let credentialOptions = models.credentialOptions;
+
+ vm.form = credentialOptions.createFormSchema('put', {
+ omit: ['user', 'team', 'inputs'],
+ models
+ });
+
+ vm.form.credential_type._data = credentialType.get('results');
+ vm.form.credential_type._placeholder = 'SELECT A TYPE';
+ vm.form.credential_type._format = 'grouped-object';
+ vm.form.credential_type._display = 'name';
+ vm.form.credential_type._key = 'id';
+ vm.form.credential_type._exp = 'type as type.name group by type.kind for type in state._data';
+
+ vm.form.inputs = {
+ _get: credentialType.mergeInputProperties,
+ _source: vm.form.credential_type,
+ _reference: 'vm.form.inputs',
+ _key: 'inputs'
+ };
+
+ vm.form.save = data => {
+ data.user = me.get('results[0].id');
+
+ return credential.request('post', data);
+ };
+
+ vm.form.onSaveSuccess = res => {
+ $state.go('credentials.edit', { credential: res.data });
+ };
+}
+
+EditCredentialsController.$inject = [
+ 'resolvedModels',
+ '$state'
+];
+
+export default EditCredentialsController;
diff --git a/awx/ui/client/features/credentials/index.js b/awx/ui/client/features/credentials/index.js
index 5846dc5fa1..c4bee3db68 100644
--- a/awx/ui/client/features/credentials/index.js
+++ b/awx/ui/client/features/credentials/index.js
@@ -1,6 +1,7 @@
import CredentialList from './credentials.list.js';
import ListController from './list/credentials-list.controller';
import AddController from './add-credentials.controller.js';
+import EditController from './edit-credentials.controller.js';
import { N_ } from '../../src/i18n';
function config ($stateExtenderProvider, pathServiceProvider) {
@@ -41,32 +42,20 @@ function config ($stateExtenderProvider, pathServiceProvider) {
});
function CredentialsResolve ($q, params, Me, Credential, CredentialType) {
- let models;
- let id = params.id;
let promises = {
me: new Me('get')
};
- if (!id) {
+ if (params.credential) {
+ promises.credential = new Credential('get', params.credential);
+ promises.credentialOptions = new Credential('options');
+ promises.credentialType = new CredentialType('get', params.credential.credential_type);
+ } else {
promises.credential = new Credential('options');
promises.credentialType = new CredentialType('get');
-
- return $q.all(promises).then(models => models);
}
-
- promises.credential = new Credential('get', id);
- return $q.all(promises)
- .then(_models_ => {
- models = _models_;
-
- return new CredentialType('get', models.credential.get('id'));
- })
- .then(credentialType => {
- models.credentialType = credentialType;
-
- return models;
- });
+ return $q.all(promises).then(models => models);
}
CredentialsResolve.$inject = [
@@ -85,7 +74,7 @@ function config ($stateExtenderProvider, pathServiceProvider) {
},
views: {
'add@credentials': {
- templateUrl: pathService.getViewPath('credentials/add-credentials'),
+ templateUrl: pathService.getViewPath('credentials/add-edit-credentials'),
controller: AddController,
controllerAs: 'vm'
}
@@ -97,14 +86,14 @@ function config ($stateExtenderProvider, pathServiceProvider) {
stateExtender.addState({
name: 'credentials.edit',
- route: '/edit/:id',
+ route: '/edit/:credential',
ncyBreadcrumb: {
label: N_('EDIT')
},
views: {
'edit@credentials': {
- templateUrl: pathService.getViewPath('credentials/add-credentials'),
- controller: AddController,
+ templateUrl: pathService.getViewPath('credentials/add-edit-credentials'),
+ controller: EditController,
controllerAs: 'vm'
}
},
@@ -124,4 +113,5 @@ angular
.config(config)
.factory('CredentialList', CredentialList)
.controller('ListController', ListController)
- .controller('AddController', AddController);
+ .controller('AddController', AddController)
+ .controller('EditController', EditController);
diff --git a/awx/ui/client/lib/components/_index.less b/awx/ui/client/lib/components/_index.less
index 943a2ecd20..4a313024b8 100644
--- a/awx/ui/client/lib/components/_index.less
+++ b/awx/ui/client/lib/components/_index.less
@@ -1,5 +1,6 @@
@import 'action/_index';
@import 'input/_index';
@import 'panel/_index';
+@import 'modal/_index';
@import 'popover/_index';
@import 'tabs/_index';
diff --git a/awx/ui/client/lib/components/form/form.directive.js b/awx/ui/client/lib/components/form/form.directive.js
index f3d9b85ca0..041a4cc9d4 100644
--- a/awx/ui/client/lib/components/form/form.directive.js
+++ b/awx/ui/client/lib/components/form/form.directive.js
@@ -12,10 +12,11 @@ function AtFormController (eventService) {
let form;
vm.components = [];
+ vm.modal = {};
vm.state = {
isValid: false,
disabled: false,
- value: {}
+ value: {},
};
vm.init = (_scope_, _form_) => {
@@ -76,7 +77,7 @@ function AtFormController (eventService) {
}, {});
scope.state.save(data)
- .then(scope.state.created)
+ .then(scope.state.onSaveSuccess)
.catch(err => vm.onSaveError(err))
.finally(() => vm.state.disabled = false);
};
@@ -89,7 +90,15 @@ function AtFormController (eventService) {
}
if (!handled) {
- // TODO: launch modal for unexpected error type
+ let message;
+
+ if (typeof err.data === 'object') {
+ message = JSON.stringify(err.data);
+ } else {
+ message = err.data;
+ }
+
+ vm.modal.show('Unable to Submit', `Unexpected Error: ${message}`);
}
};
diff --git a/awx/ui/client/lib/components/form/form.partial.html b/awx/ui/client/lib/components/form/form.partial.html
index 3303ed535e..dd2d00b40e 100644
--- a/awx/ui/client/lib/components/form/form.partial.html
+++ b/awx/ui/client/lib/components/form/form.partial.html
@@ -1,5 +1,9 @@
-
+
diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js
index ecf1d729c9..fda8307819 100644
--- a/awx/ui/client/lib/components/index.js
+++ b/awx/ui/client/lib/components/index.js
@@ -10,6 +10,7 @@ import inputSecret from './input/secret.directive';
import inputText from './input/text.directive';
import inputTextarea from './input/textarea.directive';
import inputTextareaSecret from './input/textarea-secret.directive';
+import modal from './modal/modal.directive';
import panel from './panel/panel.directive';
import panelHeading from './panel/heading.directive';
import panelBody from './panel/body.directive';
@@ -33,6 +34,7 @@ angular
.directive('atInputText', inputText)
.directive('atInputTextarea', inputTextarea)
.directive('atInputTextareaSecret', inputTextareaSecret)
+ .directive('atModal', modal)
.directive('atPanel', panel)
.directive('atPanelHeading', panelHeading)
.directive('atPanelBody', panelBody)
diff --git a/awx/ui/client/lib/components/input/select.directive.js b/awx/ui/client/lib/components/input/select.directive.js
index b9e1b6588c..348480fc54 100644
--- a/awx/ui/client/lib/components/input/select.directive.js
+++ b/awx/ui/client/lib/components/input/select.directive.js
@@ -36,6 +36,7 @@ function AtInputSelectController (baseInputController, eventService) {
[select, 'focus', () => input.classList.add('at-Input--focus')],
[select, 'change', () => scope.$apply(() => {
scope.open = false;
+ vm.updateDisplayModel();
vm.check();
})],
[select, 'blur', () => {
diff --git a/awx/ui/client/lib/components/input/select.partial.html b/awx/ui/client/lib/components/input/select.partial.html
index 14b67acafa..d096c4baef 100644
--- a/awx/ui/client/lib/components/input/select.partial.html
+++ b/awx/ui/client/lib/components/input/select.partial.html
@@ -8,14 +8,12 @@
placeholder="{{state._placeholder || undefined }}"
ng-class="{ 'at-Input--rejected': state._rejected }"
ng-model="displayModel"
- ng-disabled="state._disabled || form.disabled"
- ng-change="vm.check()" />
+ ng-disabled="state._disabled || form.disabled" />
diff --git a/awx/ui/client/lib/components/modal/_index.less b/awx/ui/client/lib/components/modal/_index.less
new file mode 100644
index 0000000000..11e962e98b
--- /dev/null
+++ b/awx/ui/client/lib/components/modal/_index.less
@@ -0,0 +1,10 @@
+.at-Modal-title {
+ margin: 0;
+ padding: 0;
+
+ .at-mixin-Heading(@at-font-size-3x);
+}
+
+.at-Modal-body {
+ font-size: @at-font-size;
+}
diff --git a/awx/ui/client/lib/components/modal/modal.directive.js b/awx/ui/client/lib/components/modal/modal.directive.js
index e69de29bb2..10f18a0afc 100644
--- a/awx/ui/client/lib/components/modal/modal.directive.js
+++ b/awx/ui/client/lib/components/modal/modal.directive.js
@@ -0,0 +1,63 @@
+const DEFAULT_ANIMATION_DURATION = 150;
+
+function atModalLink (scope, el, attr, controllers) {
+ let modalController = controllers[0];
+ let container = el[0];
+
+ modalController.init(scope, container);
+}
+
+function AtModalController () {
+ let vm = this;
+
+ let scope;
+ let container;
+
+ vm.init = (_scope_, _container_) => {
+ scope = _scope_;
+ container = _container_;
+
+ scope.state.show = vm.show;
+ scope.state.hide = vm.hide;
+ };
+
+ vm.show = (title, message) => {
+ scope.title = title;
+ scope.message = message;
+
+ container.style.display = 'block';
+ container.style.opacity = 1;
+ };
+
+ vm.hide = () => {
+ container.style.opacity = 0;
+
+ setTimeout(() => {
+ container.style.display = 'none';
+ scope.message = '';
+ scope.title = '';
+ }, DEFAULT_ANIMATION_DURATION);
+ };
+}
+
+function atModal (pathService) {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ require: ['atModal'],
+ templateUrl: pathService.getPartialPath('components/modal/modal'),
+ controller: AtModalController,
+ controllerAs: 'vm',
+ link: atModalLink,
+ scope: {
+ state: '='
+ }
+ };
+}
+
+atModal.$inject = [
+ 'PathService'
+];
+
+export default atModal;
diff --git a/awx/ui/client/lib/components/modal/modal.partial.html b/awx/ui/client/lib/components/modal/modal.partial.html
index e69de29bb2..69df2bc984 100644
--- a/awx/ui/client/lib/components/modal/modal.partial.html
+++ b/awx/ui/client/lib/components/modal/modal.partial.html
@@ -0,0 +1,20 @@
+
diff --git a/awx/ui/client/lib/components/popover/popover.directive.js b/awx/ui/client/lib/components/popover/popover.directive.js
index 296a6e49fb..66857f6e4c 100644
--- a/awx/ui/client/lib/components/popover/popover.directive.js
+++ b/awx/ui/client/lib/components/popover/popover.directive.js
@@ -17,7 +17,7 @@ function AtPopoverController () {
vm.init = (scope, _container_, _icon_, _popover_) => {
icon = _icon_;
popover = _popover_;
- scope.inline = scope.state._inline || true;
+ scope.inline = true;
icon.addEventListener('click', vm.createDisplayListener());
};
diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js
index c01b924758..594a8087d9 100644
--- a/awx/ui/client/lib/models/Base.js
+++ b/awx/ui/client/lib/models/Base.js
@@ -1,9 +1,17 @@
let $http;
+let $q;
function request (method, ...args) {
- method = method.toUpperCase();
+ this.method = method.toUpperCase();
- switch (method) {
+ if (typeof args[0] === 'object') {
+ this.res = null;
+ this.model = args[0];
+
+ return $q.resolve();
+ }
+
+ switch (this.method) {
case 'OPTIONS':
return this.httpOptions(...args);
case 'GET':
@@ -112,12 +120,13 @@ function BaseModel (path) {
this.path = this.normalizePath(path);
};
-function BaseModelLoader (_$http_) {
+function BaseModelLoader (_$http_, _$q_) {
$http = _$http_;
+ $q = _$q_;
return BaseModel;
}
-BaseModelLoader.$inject = ['$http'];
+BaseModelLoader.$inject = ['$http', '$q'];
export default BaseModelLoader;
diff --git a/awx/ui/client/lib/models/Credential.js b/awx/ui/client/lib/models/Credential.js
index 1f36c7c5c2..c99c0f0099 100644
--- a/awx/ui/client/lib/models/Credential.js
+++ b/awx/ui/client/lib/models/Credential.js
@@ -1,7 +1,9 @@
let BaseModel;
-function createFormSchema (type, config) {
- let schema = Object.assign({}, this.get('actions.POST'));
+function createFormSchema (method, config) {
+ method = method.toUpperCase();
+
+ let schema = Object.assign({}, this.get(`actions.${method}`));
if (config && config.omit) {
config.omit.forEach(key => {