diff --git a/awx/ui/.eslintignore b/awx/ui/.eslintignore
index 9222615c2c..d806c9acc1 100644
--- a/awx/ui/.eslintignore
+++ b/awx/ui/.eslintignore
@@ -11,3 +11,5 @@ templates
tests
client/**/*.js
+!client/components/**/*.js
+
diff --git a/awx/ui/.eslintrc.js b/awx/ui/.eslintrc.js
index 9eabfdcf47..6d1174fa55 100644
--- a/awx/ui/.eslintrc.js
+++ b/awx/ui/.eslintrc.js
@@ -15,8 +15,9 @@ module.exports = {
jsyaml: true
},
rules: {
- indent: ['error', 4],
- 'comma-dangle': 'off',
- 'prefer-const': ['off']
+ indent: [0, 4],
+ 'comma-dangle': 0,
+ 'prefer-const': 0,
+ 'space-before-function-paren': [2, 'always']
}
};
diff --git a/awx/ui/client/components/_index.less b/awx/ui/client/components/_index.less
new file mode 100644
index 0000000000..51028d0369
--- /dev/null
+++ b/awx/ui/client/components/_index.less
@@ -0,0 +1 @@
+@import '_panel';
diff --git a/awx/ui/client/components/_panel.less b/awx/ui/client/components/_panel.less
new file mode 100644
index 0000000000..2f73622fcb
--- /dev/null
+++ b/awx/ui/client/components/_panel.less
@@ -0,0 +1,9 @@
+.at-Panel {
+ margin: @at-margin-md 0;
+}
+
+// TODO (remove override on cleanup):
+.at-Panel-heading:hover {
+ cursor: default;
+}
+// TODO-end
diff --git a/awx/ui/client/components/index.js b/awx/ui/client/components/index.js
new file mode 100644
index 0000000000..b327c46d39
--- /dev/null
+++ b/awx/ui/client/components/index.js
@@ -0,0 +1,8 @@
+import panel from './panel.directive';
+import panelHeading from './panel-heading.directive';
+
+angular
+ .module('at.components', [])
+ .directive('atPanel', panel)
+ .directive('atPanelHeading', panelHeading);
+
diff --git a/awx/ui/client/components/panel-heading.directive.js b/awx/ui/client/components/panel-heading.directive.js
new file mode 100644
index 0000000000..3718f19768
--- /dev/null
+++ b/awx/ui/client/components/panel-heading.directive.js
@@ -0,0 +1,12 @@
+function atPanelHeading () {
+ return {
+ restrict: 'E',
+ transclude: true,
+ templateUrl: 'static/partials/components/panel-heading.partial.html',
+ scope: {
+ heading: '=config'
+ }
+ };
+}
+
+export default atPanelHeading;
diff --git a/awx/ui/client/components/panel-heading.partial.html b/awx/ui/client/components/panel-heading.partial.html
new file mode 100644
index 0000000000..760b4c8c42
--- /dev/null
+++ b/awx/ui/client/components/panel-heading.partial.html
@@ -0,0 +1,13 @@
+
+
+
+ {{ heading.title }}
+
+
+ {{ heading.titleBadge }}
+
+
+
+
+
+
diff --git a/awx/ui/client/components/panel.directive.js b/awx/ui/client/components/panel.directive.js
index e69de29bb2..3ea08ba67f 100644
--- a/awx/ui/client/components/panel.directive.js
+++ b/awx/ui/client/components/panel.directive.js
@@ -0,0 +1,9 @@
+function atPanel () {
+ return {
+ restrict: 'E',
+ transclude: true,
+ templateUrl: 'static/partials/components/panel.partial.html'
+ };
+}
+
+export default atPanel;
diff --git a/awx/ui/client/components/panel.partial.html b/awx/ui/client/components/panel.partial.html
new file mode 100644
index 0000000000..fdd858378f
--- /dev/null
+++ b/awx/ui/client/components/panel.partial.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js
index 02b13f0f40..46733e7d68 100644
--- a/awx/ui/client/src/app.js
+++ b/awx/ui/client/src/app.js
@@ -60,7 +60,6 @@ import login from './login/main';
import activityStream from './activity-stream/main';
import standardOut from './standard-out/main';
import Templates from './templates/main';
-import credentials from './credentials/main';
import jobs from './jobs/main';
import teams from './teams/main';
import users from './users/main';
@@ -72,6 +71,9 @@ import footer from './footer/main';
import scheduler from './scheduler/main';
import instanceGroups from './instance-groups/main';
+import '../components';
+import './credentials';
+
var tower = angular.module('Tower', [
// how to add CommonJS / AMD third-party dependencies:
// 1. npm install --save package-name
@@ -118,7 +120,6 @@ var tower = angular.module('Tower', [
standardOut.name,
Templates.name,
portalMode.name,
- credentials.name,
jobs.name,
teams.name,
users.name,
@@ -131,6 +132,8 @@ var tower = angular.module('Tower', [
'PromptDialog',
'AWDirectives',
'features',
+ 'at.components',
+ 'at.feature.credentials'
])
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
diff --git a/awx/ui/client/src/credentials/index.controller.js b/awx/ui/client/src/credentials/index.controller.js
new file mode 100644
index 0000000000..60cb0ec65b
--- /dev/null
+++ b/awx/ui/client/src/credentials/index.controller.js
@@ -0,0 +1,12 @@
+function IndexController () {
+ let vm = this;
+
+ vm.panel = {
+ title: 'Credentials',
+ titleBadge: 5
+ };
+}
+
+// IndexController.$inject = [];
+
+export default IndexController;
diff --git a/awx/ui/client/src/credentials/index.js b/awx/ui/client/src/credentials/index.js
new file mode 100644
index 0000000000..8d2fac4063
--- /dev/null
+++ b/awx/ui/client/src/credentials/index.js
@@ -0,0 +1,17 @@
+import IndexController from './index.controller';
+
+function routes ($stateProvider) {
+ $stateProvider.state({
+ name: 'credentials',
+ url: '/credentials',
+ templateUrl: '/static/views/credentials/index.view.html',
+ controller: IndexController,
+ controllerAs: 'vm'
+ });
+}
+
+routes.$inject = [
+ '$stateProvider'
+];
+
+angular.module('at.feature.credentials', []).config(routes);
diff --git a/awx/ui/client/src/credentials/index.view.html b/awx/ui/client/src/credentials/index.view.html
new file mode 100644
index 0000000000..7b321e801a
--- /dev/null
+++ b/awx/ui/client/src/credentials/index.view.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/awx/ui/client/src/credentials/main.js b/awx/ui/client/src/credentials/main.js
deleted file mode 100644
index 0dfe1d8d5d..0000000000
--- a/awx/ui/client/src/credentials/main.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*************************************************
- * Copyright (c) 2016 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-import ownerList from './ownerList.directive';
-import CredentialsList from './list/credentials-list.controller';
-import CredentialsAdd from './add/credentials-add.controller';
-import CredentialsEdit from './edit/credentials-edit.controller';
-import BecomeMethodChange from './factories/become-method-change.factory';
-import CredentialFormSave from './factories/credential-form-save.factory';
-import KindChange from './factories/kind-change.factory';
-import OwnerChange from './factories/owner-change.factory';
-import CredentialList from './credentials.list';
-import CredentialForm from './credentials.form';
-import { N_ } from '../i18n';
-
-export default
- angular.module('credentials', [])
- .directive('ownerList', ownerList)
- .factory('BecomeMethodChange', BecomeMethodChange)
- .factory('CredentialFormSave', CredentialFormSave)
- .factory('KindChange', KindChange)
- .factory('OwnerChange', OwnerChange)
- .controller('CredentialsList', CredentialsList)
- .controller('CredentialsAdd', CredentialsAdd)
- .controller('CredentialsEdit', CredentialsEdit)
- .factory('CredentialList', CredentialList)
- .factory('CredentialForm', CredentialForm)
- .config(['$stateProvider', 'stateDefinitionsProvider',
- function($stateProvider, stateDefinitionsProvider) {
- let stateDefinitions = stateDefinitionsProvider.$get();
-
- // lazily generate a tree of substates which will replace this node in ui-router's stateRegistry
- // see: stateDefinition.factory for usage documentation
- $stateProvider.state({
- name: 'credentials',
- url: '/credentials',
- lazyLoad: () => stateDefinitions.generateTree({
- parent: 'credentials',
- modes: ['add', 'edit'],
- list: 'CredentialList',
- form: 'CredentialForm',
- controllers: {
- list: CredentialsList,
- add: CredentialsAdd,
- edit: CredentialsEdit
- },
- data: {
- activityStream: true,
- activityStreamTarget: 'credential'
- },
- ncyBreadcrumb: {
- parent: 'setup',
- label: N_('CREDENTIALS')
- }
- })
- });
- }
- ]);
diff --git a/awx/ui/client/theme/_common.less b/awx/ui/client/theme/_common.less
new file mode 100644
index 0000000000..705343a3ec
--- /dev/null
+++ b/awx/ui/client/theme/_common.less
@@ -0,0 +1,22 @@
+.at-Title-row {
+ align-items: center;
+ flex: 1 0 auto;
+ display: flex;
+}
+
+.at-Title-text {
+ color: @at-gray-dark;
+ font-size: @at-font-md;
+ font-weight: bold;
+ margin-right: @at-margin-sm;
+ text-transform: uppercase;
+}
+
+.at-Title-badge {
+ font-size: @at-font-sm;
+ padding: 0 @at-padding-sm;
+ margin: 0;
+ background-color: @at-gray;
+ color: @at-white;
+ border-radius: @at-border-radius-md;
+}
diff --git a/awx/ui/client/theme/_variables.less b/awx/ui/client/theme/_variables.less
new file mode 100644
index 0000000000..f14a662e53
--- /dev/null
+++ b/awx/ui/client/theme/_variables.less
@@ -0,0 +1,16 @@
+@at-white: #ffffff;
+@at-gray: #848992;
+@at-gray-dark: #707070;
+
+@at-font-sm: 12px;
+@at-font-md: 14px;
+
+@at-padding-sm: 10px;
+@at-padding-md: 20px;
+
+@at-margin-sm: 10px;
+@at-margin-md: 20px;
+
+@at-border-radius-md: 5px;
+
+
diff --git a/awx/ui/client/theme/index.less b/awx/ui/client/theme/index.less
new file mode 100644
index 0000000000..cb5a44e92b
--- /dev/null
+++ b/awx/ui/client/theme/index.less
@@ -0,0 +1,4 @@
+@import '_variables';
+@import '_common';
+
+@import '../components/_index';
diff --git a/awx/ui/grunt-tasks/browserSync.js b/awx/ui/grunt-tasks/browserSync.js
index efb32e31f9..9dc0272d48 100644
--- a/awx/ui/grunt-tasks/browserSync.js
+++ b/awx/ui/grunt-tasks/browserSync.js
@@ -18,6 +18,7 @@ module.exports = {
},
keepalive: false,
watchTask: true,
+ reloadDebounce: 1000,
// The browser-sync-client lib will write your current scroll position to window.name
// https://github.com/BrowserSync/browser-sync-client/blob/a2718faa91e11553feca7a3962313bf1ec6ba3e5/dist/index.js#L500
// This strategy is enabled in the core browser-sync lib, and not externally documented as an option. Yay!
diff --git a/awx/ui/grunt-tasks/concurrent.js b/awx/ui/grunt-tasks/concurrent.js
index 45085340bf..3cafeba559 100644
--- a/awx/ui/grunt-tasks/concurrent.js
+++ b/awx/ui/grunt-tasks/concurrent.js
@@ -1,16 +1,16 @@
module.exports = {
dev: {
- tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:languages', 'copy:config', 'less:dev'],
+ tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:views', 'copy:languages', 'copy:config', 'less:dev'],
},
// This concurrent target is intended for development ui builds that do not require raising browser-sync or filesystem polling
devNoSync: {
- tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:languages', 'copy:config', 'less:dev', 'webpack:dev'],
+ tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:views', 'copy:languages', 'copy:config', 'less:dev', 'webpack:dev'],
},
prod: {
- tasks: ['newer:copy:vendor', 'newer:copy:assets', 'newer:copy:partials', 'newer:copy:languages', 'newer:copy:config', 'newer:less:prod']
+ tasks: ['newer:copy:vendor', 'newer:copy:assets', 'newer:copy:partials', 'newer:copy:views', 'newer:copy:languages', 'newer:copy:config', 'newer:less:prod']
},
watch: {
- tasks: ['watch:css', 'watch:partials', 'watch:assets', ['webpack:dev', 'watch:config']],
+ tasks: ['watch:css', 'watch:partials', 'watch:views', 'watch:assets', ['webpack:dev', 'watch:config']],
options: {
logConcurrentOutput: true
}
diff --git a/awx/ui/grunt-tasks/copy.js b/awx/ui/grunt-tasks/copy.js
index 5e32be24b3..fc66d09ea6 100644
--- a/awx/ui/grunt-tasks/copy.js
+++ b/awx/ui/grunt-tasks/copy.js
@@ -30,6 +30,14 @@ module.exports = {
dest: 'static/lib/'
}]
},
+ views: {
+ files: [{
+ cwd: 'client/src',
+ expand: true,
+ src: ['**/*.view.html'],
+ dest: 'static/views/'
+ }]
+ },
partials: {
files: [{
cwd: 'client/src',
@@ -41,6 +49,11 @@ module.exports = {
expand: true,
src: ['*.html'],
dest: 'static/partials/'
+ }, {
+ cwd: 'client/components',
+ expand: true,
+ src: ['*.partial.html'],
+ dest: 'static/partials/components/'
}]
},
languages: {
diff --git a/awx/ui/grunt-tasks/less.js b/awx/ui/grunt-tasks/less.js
index d7c6d98245..782b7787d4 100644
--- a/awx/ui/grunt-tasks/less.js
+++ b/awx/ui/grunt-tasks/less.js
@@ -10,18 +10,19 @@ module.exports = {
src: [
'client/legacy-styles/*.less',
'client/src/**/*.less',
+ 'client/theme/index.less'
]
}],
options: {
sourceMap: true
}
},
-
prod: {
files: {
'static/tower.min.css': [
'client/legacy-styles/*.less',
'client/src/**/*.less',
+ 'client/theme/index.less'
]
},
options: {
diff --git a/awx/ui/grunt-tasks/watch.js b/awx/ui/grunt-tasks/watch.js
index ba0b852038..91f1b1b72e 100644
--- a/awx/ui/grunt-tasks/watch.js
+++ b/awx/ui/grunt-tasks/watch.js
@@ -1,12 +1,19 @@
module.exports = {
css: {
files: 'client/**/*.less',
- tasks: ['newer:less:dev']
+ tasks: ['less:dev']
},
partials: {
- files: 'client/src/**/*.html',
+ files: [
+ 'client/components/*.partial.html',
+ 'client/src/**/*.partial.html'
+ ],
tasks: ['newer:copy:partials']
},
+ views: {
+ files: 'client/src/**/*.view.html',
+ tasks: ['newer:copy:views']
+ },
assets: {
files: 'client/assets',
tasks: ['newer:copy:assets']