1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 15:21:13 +03:00

implement componenitized navigation and remove old nav and layout code

This commit is contained in:
John Mitchell 2017-09-06 17:32:32 -04:00
parent 2daaef2a99
commit ea91fabba0
No known key found for this signature in database
GPG Key ID: FE6A9B5BD4EB5C94
49 changed files with 677 additions and 1540 deletions

View File

@ -10,7 +10,6 @@ function LegacyCredentialsService (pathService) {
name: 'credentials',
route: '/credentials',
ncyBreadcrumb: {
parent: 'setup',
label: N_('CREDENTIALS')
},
data: {

View File

@ -600,15 +600,6 @@ dd {
text-decoration: none;
}
.navbar.site-footer {
border-top-left-radius: 0;
border-top-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin-bottom: 0;
background-color: @black;
}
.login-header img {
width: 60%;
}
@ -2309,3 +2300,37 @@ input[disabled].ui-spinner-input {
.ui-dialog .ui-dialog-content {
background: @default-bg;
}
html { height: 100%; }
body {
font-family: 'Open Sans', sans-serif;
font-weight: 400;
color: @default-data-txt;
background-color: @default-secondary-bg;
}
.container-fluid {
padding-left: 20px;
padding-right: 20px;
}
#content-container {
padding-bottom: 0px;
}
.Panel {
background-color: @panel-bg;
border-radius: 5px;
padding: 20px;
border: 1px solid @panel-border;
margin-top: 20px;
}
.Panel-hidden {
display: none;
}
.btn {
text-transform: uppercase;
}

View File

@ -1,84 +0,0 @@
/*********************************************
* Copyright (c) 2015 Ansible, Inc.
*
* breadcrumbs.less
*
* custom breadcrumbs
*
* JT-- 7/22/14 -- changed the breadcrumbs to be all white with grey outline
*
*/
.ansible-breadcrumb {
list-style: none;
overflow: hidden;
padding: 0;
margin: 0 0 15px 0;
}
.ansible-breadcrumb li {
float: left;
height: 26px;
margin-top: 3px;
margin-bottom: 3px;
}
.ansible-breadcrumb li a {
color: @black;
font-weight: normal;
text-decoration: none;
padding: 3px 8px 3px 16px;
background: @white; /* fallback color */
position: relative;
left: 0;
top: 0;
display: block;
border-style: solid;
border-color: @grey;
border-width: thin;
float: left;
}
.ansible-breadcrumb li:first-child a{
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
padding-left: 11px;
}
.ansible-breadcrumb li.active a {
background: @white;
color: @black;
font-weight: bold;
}
.ansible-breadcrumb li a:after {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 13px dashed transparent; /* Go big on the size, and let overflow hide */
border-bottom: 13px dashed transparent;
border-left: 10px solid @white;
position: absolute;
top: 50%;
margin-top: -13px;
left: 100%;
z-index: 2;
}
.ansible-breadcrumb li.active a:after {
border-left: 10px solid @white
}
.ansible-breadcrumb li a:before {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 13px dashed transparent;
border-bottom: 13px dashed transparent;
border-left: 11px solid @grey;
position: absolute;
top: 50%;
margin-top: -13px;
margin-left: 1px;
left: 100%;
z-index: 1;
}
.ansible-breadcrumb li.active a:before {
border-left: 11px solid @grey;
}

View File

@ -1,183 +0,0 @@
/*********************************************
* Copyright (c) 2015 Ansible, Inc.
*
* jPushMenu.less
*
* Custom styles for slideout menu
*
*/
.cbp-spmenu {
background: #E8E8E8;
position: fixed;
}
.cbp-spmenu h3 {
color: @white;
font-size: 14px;
padding: 15px 10px 15px 10px;
margin: 0;
font-weight: 600;
background: @grey;
}
.cbp-spmenu a {
display: block;
color: @grey-txt;
font-size: 1.1em;
font-weight: 300;
}
.cbp-spmenu a:hover {
background: #D8D8D8;
color: @black;
}
.cbp-spmenu a.active {
background: #E8E8E8;
color: @black;
font-weight: 600;
}
/* Orientation-dependent styles for the content of the menu */
.cbp-spmenu-vertical {
width: 240px;
height: 100%;
top: 0;
z-index: 1100;
overflow-y:auto;
}
.cbp-spmenu-vertical a {
border-bottom: 1px solid #D8D8D8;
padding: 10px 10px 10px 20px;
}
.cbp-spmenu-horizontal {
width: 100%;
height: 150px;
left: 0;
z-index: 1000;
overflow: hidden;
}
.cbp-spmenu-horizontal h3 {
height: 100%;
width: 20%;
float: left;
}
.cbp-spmenu-horizontal a {
float: left;
width: 20%;
padding: 0.8em;
border-left: 1px solid #258ecd;
}
/* Vertical menu that slides from the left or right */
.cbp-spmenu-left {
left: -240px;
}
.cbp-spmenu-right {
right: -240px;
}
.cbp-spmenu-open.cbp-spmenu-open {
-moz-box-shadow: 3px 3px 5px 6px #ccc;
-webkit-box-shadow: 3px 3px 5px 6px #ccc;
box-shadow: 3px 3px 5px 6px #ccc;
}
.cbp-spmenu-left.cbp-spmenu-open {
left: 0px;
}
.cbp-spmenu-right.cbp-spmenu-open {
right: 0px;
}
/* Horizontal menu that slides from the top or bottom */
.cbp-spmenu-top {
top: -150px;
}
.cbp-spmenu-bottom {
bottom: -150px;
}
.cbp-spmenu-top.cbp-spmenu-open {
top: 0px;
}
.cbp-spmenu-bottom.cbp-spmenu-open {
bottom: 0px;
}
/* Push classes applied to the body */
.cbp-spmenu-push {
position: relative;
overflow-x: hidden;
left: 0;
}
.cbp-spmenu-push-toright {
left: 240px;
}
.cbp-spmenu-push-toleft {
left: -240px;
}
/* Transitions */
.cbp-spmenu,
.cbp-spmenu-push {
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
transition: all 0.3s ease;
}
/* Example media queries */
@media screen and (max-width: 55.1875em){
.cbp-spmenu-horizontal {
font-size: 75%;
height: 110px;
}
.cbp-spmenu-top {
top: -110px;
}
.cbp-spmenu-bottom {
bottom: -110px;
}
}
@media screen and (max-height: 26.375em){
.cbp-spmenu-vertical {
font-size: 90%;
width: 190px;
}
.cbp-spmenu-left,
.cbp-spmenu-push-toleft {
left: -190px;
}
.cbp-spmenu-right {
right: -190px;
}
.cbp-spmenu-push-toright {
left: 190px;
}
}

View File

@ -1,127 +0,0 @@
/*********************************************
* Copyright (c) 2015 Ansible, Inc.
*
* main-layout.css
*
* primary page layout styles
*
*/
html { height: 100%; }
body {
font-family: 'Open Sans', sans-serif;
font-weight: 400;
color: @default-data-txt;
padding-top: 100px;
min-height: 100%;
padding-bottom: 50px;
position: relative;
background-color: @default-secondary-bg;
padding-top: 100px;
}
.container-fluid {
padding-left: 20px;
padding-right: 20px;
}
#main-menu-container {
.navbar {
margin-bottom: 0;
}
#ansible-brand-logo {
width: 160px;
height: 53px;
margin-top: 5px;
margin-left: 10px;
}
/*.navbar-collapse {
margin-left: 47px;
}*/
.collapsed-option {
display: none;
}
.nav > li > a {
padding-top: 11px;
padding-bottom: 11px;
}
}
#ansible-list-title {
display: none;
}
#content-container {
padding-bottom: 0px;
}
.group-breadcrumbs {
margin-bottom: 20px;
}
#socket-beacon-div {
display: none;
}
#socket-beacon-li {
display: block;
}
#account-submenu {
margin-right: 5px;
}
.site-footer {
height: 15px;
}
.Panel {
background-color: @panel-bg;
border-radius: 5px;
padding: 20px;
border: 1px solid @panel-border;
margin-top: 20px;
}
.Panel-hidden {
display: none;
}
.btn{
text-transform: uppercase;
}
@media (max-width: 1075px) {
#main-menu-container {
.collapsed-option {
display: block;
}
}
#ansible-list-title {
display: inline-block;
position: relative;
top: 1px;
left: 10px;
color: @default-data-txt;
text-transform:capitalize;
}
#account-menu {
display: none;
}
#account-submenu {
margin-left: 20px;
}
#socket-beacon-div {
display: block;
position: fixed;
right: 30px;
top: 5px;
margin-right: 50px;
margin-top: 15px;
}
#socket-beacon-li {
display: none;
}
}

View File

@ -1,5 +1,6 @@
@import 'action/_index';
@import 'input/_index';
@import 'layout/_index';
@import 'modal/_index';
@import 'panel/_index';
@import 'popover/_index';

View File

@ -47,6 +47,31 @@ function ComponentsStrings (BaseString) {
DEFAULT: t.s('Copy full revision to clipboard.'),
COPIED: t.s('Copied to clipboard.')
}
ns.layout = {
CURRENT_USER_LABEL: t.s('Logged in as'),
VIEW_DOCS: t.s('View Documentation'),
LOGOUT: t.s('Logout'),
DASHBOARD: t.s('Dashboard'),
JOBS: t.s('Jobs'),
SCHEDULES: t.s('Schedules'),
PORTAL_MODE: t.s('Portal Mode'),
PROJECTS: t.s('Projects'),
CREDENTIALS: t.s('Credentials'),
CREDENTIAL_TYPES: t.s('Credential Types'),
INVENTORIES: t.s('Inventories'),
TEMPLATES: t.s('Templates'),
ORGANIZATIONS: t.s('Organizations'),
USERS: t.s('Users'),
TEAMS: t.s('Teams'),
INVENTORY_SCRIPTS: t.s('Inventory Scripts'),
NOTIFICATIONS: t.s('Notifications'),
MANAGEMENT_JOBS: t.s('Management Jobs'),
INSTANCE_GROUPS: t.s('Instance Groups'),
SETTINGS: t.s('Settings'),
FOOTER_ABOUT: t.s('About'),
FOOTER_COPYRIGHT: t.s('Copyright © 2017 Red Hat, Inc.')
}
}
ComponentsStrings.$inject = ['BaseStringService'];

View File

@ -1,3 +1,7 @@
import layout from './layout/layout.directive';
import topNavItem from './layout/top-nav-item.directive';
import sideNav from './layout/side-nav.directive';
import sideNavItem from './layout/side-nav-item.directive';
import actionGroup from './action/action-group.directive';
import divider from './utility/divider.directive';
import form from './form/form.directive';
@ -26,6 +30,10 @@ import ComponentsStrings from './components.strings';
angular
.module('at.lib.components', [])
.directive('atLayout', layout)
.directive('atTopNavItem', topNavItem)
.directive('atSideNav', sideNav)
.directive('atSideNavItem', sideNavItem)
.directive('atActionGroup', actionGroup)
.directive('atDivider', divider)
.directive('atForm', form)
@ -50,5 +58,3 @@ angular
.directive('atTruncate', truncate)
.service('ComponentsStrings', ComponentsStrings)
.service('BaseInputController', BaseInputController);

View File

@ -0,0 +1,172 @@
.at-Layout {
height: 100vh;
width: 100vw;
display: flex;
&-topNav {
display: flex;
background-color: @at-color-top-nav-background;
border-bottom: @at-border-default-width solid @at-color-top-nav-border-bottom;
z-index: @at-z-index-nav;
position: fixed;
right: 0;
left: 0;
top: 0;
height: @at-height-top-nav;
.at-Layout-topNavRightAligner {
margin-left: auto;
}
.at-Layout-topNavItem {
color: @at-color-top-nav-item-text;
padding: 0 @at-padding-top-nav-item-sides;
a {
cursor: pointer;
}
a, div {
color: @at-color-top-nav-item-text;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
i {
color: @at-color-top-nav-item-icon;
font-size: @at-height-top-nav-item-icon;
}
&--logo {
padding-left: 0px;
img {
max-width: @main-menu-max-width;
max-height: @main-menu-max-height;
height: @main-menu-height;
width: @main-menu-width;
margin: @main-menu-margin;
flex: initial;
}
}
&--user {
i {
margin-right: @at-margin-top-nav-item-between-icon-and-name;
}
}
&--socket {
i {
margin-top: @at-margin-top-nav-item-icon-socket-top-makeup;
font-size: @at-height-top-nav-item-icon-socket;
text-shadow:
-@at-border-default-width -@at-border-default-width 0 @at-color-top-nav-item-icon-socket-outline,
@at-border-default-width -@at-border-default-width 0 @at-color-top-nav-item-icon-socket-outline,
-@at-border-default-width @at-border-default-width 0 @at-color-top-nav-item-icon-socket-outline,
@at-border-default-width @at-border-default-width 0 @at-color-top-nav-item-icon-socket-outline
}
}
&:focus,
&:hover,
&.is-currentRoute {
background-color: @at-color-top-nav-item-background-hover;
}
&.is-loggedOut {
opacity: 0;
}
}
}
&-side {
background: @at-color-side-nav-background;
color: @at-color-side-nav-content;
position: fixed;
bottom: 0;
top: @at-height-top-side-nav-makeup;
overflow-y: auto;
min-height: 100vh;
min-width: @at-width-collapsed-side-nav;
.at-Layout-sideNavItem {
display: flex;
cursor: pointer;
i {
font-size: @at-height-side-nav-item-icon;
padding: @at-padding-side-nav-item-icon;
}
&:hover,
&.is-active {
background: @at-color-side-nav-item-background-hover;
border-left: @at-highlight-left-border-size solid @at-color-side-nav-item-border-hover;
i {
margin-left: @at-highlight-left-border-margin-makeup;
}
}
}
.at-Layout-sideNavSpacer {
height: @at-height-side-nav-spacer;
}
&--expanded {
width: @at-width-expanded-side-nav;
.at-Layout-sideNavItem {
display: flex;
justify-content: flex-start;
align-items: center;
padding-right: @at-padding-between-side-nav-icon-text;
text-transform: uppercase;
}
+ .at-Layout-main {
padding-left: @at-width-expanded-side-nav;
}
}
}
&-main {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
padding-left: @at-width-collapsed-side-nav;
overflow-x: hidden;
}
&-footer {
height: 40px;
background-color: @at-color-footer-background;
color: @at-color-footer;
z-index: 1040;
position: absolute;
right: @at-padding-footer-right;
left: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-left: (@at-width-collapsed-side-nav + 5);
a {
cursor: pointer;
margin-right: @at-margin-after-footer-link;
}
}
&-side--expanded {
+ .at-Layout-main {
.at-Layout-footer {
margin-left: @at-width-expanded-side-nav;
}
}
}
}

View File

@ -0,0 +1,52 @@
function AtLayoutController ($scope, strings) {
let vm = this || {};
$scope.$on('$stateChangeSuccess', function(event, next) {
vm.currentState = next.name;
});
$scope.$watch('$root.current_user', function(val) {
vm.isLoggedIn = val && val.username;
if (val) {
vm.isSuperUser = $scope.$root.user_is_superuser || $scope.$root.user_is_system_auditor;
vm.currentUsername = val.username;
vm.currentUserId = val.id;
}
});
$scope.$watch('$root.socketStatus', function(newStatus) {
vm.socketState = newStatus;
vm.socketIconClass = "icon-socket-" + $scope.socketStatus;
});
$scope.$watch('$root.licenseMissing', function(licenseMissing) {
vm.licenseIsMissing = licenseMissing;
});
vm.getString = function(string) {
try {
return strings.get(`layout.${string}`);
} catch(err) {
return strings.get(string);
}
};
}
AtLayoutController.$inject = ['$scope', 'ComponentsStrings'];
function atLayout (pathService) {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/layout/layout'),
controller: AtLayoutController,
controllerAs: 'vm',
scope: {
}
};
}
atLayout.$inject = ['PathService'];
export default atLayout;

View File

@ -0,0 +1,85 @@
<div class="at-Layout">
<div class="at-Layout-topNav">
<at-top-nav-item is-shown="missingLicense" class="at-Layout-topNavItem--logo">
<a href="/#/">
<img ng-src="/static/assets/logo-header.svg">
</a>
</at-top-nav-item>
<div class="at-Layout-topNavRightAligner"></div>
<at-top-nav-item class="at-Layout-topNavItem--user">
<a ng-href="/#/users/{{ $parent.layoutVm.currentUserId }}">
<i class="fa fa-user"
alt="{{ $parent.layoutVm.getString('CURRENT_USER_LABEL') }} {{ $parent.layoutVm.currentUsername }}">
</i>
<span>{{ $parent.layoutVm.currentUsername }}</span>
</a>
</at-top-nav-item>
<at-top-nav-item>
<a href="http://docs.ansible.com/ansible-tower/" target="_blank">
<i class="fa fa-book" alt="{{ $parent.layoutVm.getString('VIEW_DOCS') }}"></i>
</a>
</at-top-nav-item>
<at-top-nav-item class="at-Layout-topNavItem--socket"
ng-if="$parent.layoutVm.socketState &&
$parent.layoutVm.socketState !== 'ok'">
<div><i class="fa" ng-class="$parent.layoutVm.socketIconClass"></i></div>
</at-top-nav-item>
<at-top-nav-item is-shown="missingLicense">
<a href="/#/logout" is-always-shown="license">
<i class="fa fa-power-off" alt="{{ $parent.layoutVm.getString('LOGOUT') }}"></i>
</a>
</at-top-nav-item>
</div>
<at-side-nav>
<at-side-nav-item icon-class="fa-tachometer" route="dashboard" name="DASHBOARD">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-spinner" route="jobs" name="JOBS">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-calendar" route="jobs.schedules" name="SCHEDULES">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-columns" route="portalMode.myJobs" name="PORTAL_MODE">
</at-side-nav-item>
<div class="at-Layout-sideNavSpacer"></div>
<at-side-nav-item icon-class="fa-folder-open" route="projects" name="PROJECTS">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-key" route="credentials" name="CREDENTIALS">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-list-alt" route="credentialTypes" name="CREDENTIAL_TYPES"
system-admin-only="true">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-sitemap" route="inventories" name="INVENTORIES">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-pencil-square-o" route="templates" name="TEMPLATES">
</at-side-nav-item>
<div class="at-Layout-sideNavSpacer"></div>
<at-side-nav-item icon-class="fa-building" route="organizations" name="ORGANIZATIONS">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-user" route="users" name="USERS">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-users" route="teams" name="TEAMS">
</at-side-nav-item>
<div class="at-Layout-sideNavSpacer"></div>
<at-side-nav-item icon-class="fa-code" route="inventoryScripts" name="INVENTORY_SCRIPTS">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-bell" route="notifications" name="NOTIFICATIONS"
system-admin-only="true">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-wrench" route="managementJobsList" name="MANAGEMENT_JOBS"
system-admin-only="true">
</at-side-nav-item>
<at-side-nav-item icon-class="fa-server" route="instanceGroups" name="INSTANCE_GROUPS"
system-admin-only="true">
</at-side-nav-item>
<div class="at-Layout-sideNavSpacer"></div>
<at-side-nav-item icon-class="fa-cog" route="configuration" name="SETTINGS"
system-admin-only="true">
</at-side-nav-item>
</at-side-nav>
<div class="at-Layout-main">
<ng-transclude></ng-transclude>
<div class="at-Layout-footer">
<a ui-sref="about">{{ vm.getString('FOOTER_ABOUT') }} {{ vm.getString('BRAND_NAME') }}</a>|
{{ vm.getString('FOOTER_COPYRIGHT') }}
</div>
</div>
</div>

View File

@ -0,0 +1,51 @@
function atSideNavItemLink (scope, element, attrs, ctrl) {
scope.navVm = ctrl[0];
scope.layoutVm = ctrl[1];
}
function AtSideNavItemController ($state, $scope) {
let vm = this || {};
$scope.$watch('layoutVm.currentState', function(current) {
if ($scope.name === 'portal mode') {
vm.isRoute = (current && current.indexOf('portalMode') === 0);
} else {
if (current && current.indexOf($scope.route) === 0) {
if (current.indexOf('jobs.schedules') === 0 && $scope.route === 'jobs') {
vm.isRoute = false;
} else {
vm.isRoute = true;
}
} else {
vm.isRoute = false;
}
}
});
vm.go = function() {
$state.go($scope.route, {}, {reload: true});
}
}
AtSideNavItemController.$inject = ['$state', '$scope'];
function atSideNavItem (pathService) {
return {
restrict: 'E',
templateUrl: pathService.getPartialPath('components/layout/side-nav-item'),
require: ['^^atSideNav', '^^atLayout'],
controller: AtSideNavItemController,
controllerAs: 'vm',
link: atSideNavItemLink,
scope: {
iconClass: '@',
name: '@',
route: '@',
systemAdminOnly: '@'
}
};
}
atSideNavItem.$inject = ['PathService'];
export default atSideNavItem;

View File

@ -0,0 +1,8 @@
<div class="at-Layout-sideNavItem" ng-click="vm.go()" ng-class="{'is-active': vm.isRoute}"
ng-show="(!systemAdminOnly || layoutVm.isSuperUser) && layoutVm.isLoggedIn &&
!layoutVm.licenseIsMissing">
<i class="fa {{ iconClass }}"></i>
<span class="at-Layout-sideNavItemName" ng-show="navVm.isExpanded">
{{ layoutVm.getString(name) }}
</span>
</div>

View File

@ -0,0 +1,32 @@
function atSideNavLink (scope, element, attrs, ctrl) {
scope.layoutVm = ctrl;
}
function AtSideNavController () {
let vm = this || {};
vm.isExpanded = true;
vm.toggleExpansion = () => {
vm.isExpanded = !vm.isExpanded;
}
}
function atSideNav (pathService) {
return {
restrict: 'E',
replace: true,
require: '^^atLayout',
controller: AtSideNavController,
controllerAs: 'vm',
link: atSideNavLink,
transclude: true,
templateUrl: pathService.getPartialPath('components/layout/side-nav'),
scope: {
}
};
}
atSideNav.$inject = ['PathService'];
export default atSideNav;

View File

@ -0,0 +1,8 @@
<div class="at-Layout-side"
ng-class="{'at-Layout-side--expanded': vm.isExpanded && layoutVm.isLoggedIn}">
<div class="at-Layout-sideNavItem" ng-click="vm.toggleExpansion()"
ng-show="layoutVm.isLoggedIn && !layoutVm.licenseIsMissing">
<i class="fa fa-bars"></i>
</div>
<ng-transclude></ng-transclude>
</div>

View File

@ -0,0 +1,30 @@
function atTopNavItemLink (scope, element, attrs, ctrl) {
scope.layoutVm = ctrl;
scope.isHidden = false;
var shownWhen = attrs.isShown;
if (shownWhen !== 'missingLicense') {
scope.$watch('layoutVm.licenseIsMissing', function(val) {
scope.isHidden = val;
});
}
}
function atTopNavItem (pathService) {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/layout/top-nav-item'),
require: '^^atLayout',
link: atTopNavItemLink,
scope: {
}
};
}
atTopNavItem.$inject = ['PathService'];
export default atTopNavItem;

View File

@ -0,0 +1,3 @@
<div class="at-Layout-topNavItem" ng-class="{'is-loggedOut': !layoutVm.isLoggedIn}"
ng-show="!isHidden" ng-transclude>
</div>

View File

@ -20,6 +20,7 @@
// 1. Colors --------------------------------------------------------------------------------------
@at-gray-light-3x: #fcfcfc;
@at-gray-light-2-5x: #fafafa;
@at-gray-light-2x: #f2f2f2;
@at-gray-light: #ebebeb;
@at-gray: #e1e1e1;
@ -149,6 +150,19 @@
@at-color-table-header-background: @at-gray-light;
@at-color-line-separator: @at-gray;
@at-color-top-nav-background: @at-white;
@at-color-top-nav-border-bottom: @at-gray-dark-2x;
@at-color-top-nav-item-text: @at-gray-dark-5x;
@at-color-top-nav-item-icon: @at-gray-dark-4x;
@at-color-top-nav-item-icon-socket-outline: @at-white;
@at-color-top-nav-item-background-hover: @at-gray-light-2-5x;
@at-color-side-nav-background: @at-gray-dark-4x;
@at-color-side-nav-content: @at-white;
@at-color-side-nav-item-background-hover: @at-gray-dark-2x;
@at-color-side-nav-item-border-hover: @at-white;
@at-color-footer-background: @at-gray-light-3x;
@at-color-footer: @at-gray-dark-5x;
// 2. Typography ----------------------------------------------------------------------------------
@at-font-size-body: @at-font-size-3x;
@ -178,6 +192,10 @@
@at-padding-popover: @at-space-2x;
@at-padding-well: @at-space-2x;
@at-padding-input: @at-space-2x;
@at-padding-top-nav-item-sides: @at-space-4x;
@at-padding-side-nav-item-icon: @at-space-3x;
@at-padding-between-side-nav-icon-text: @at-space-3x;
@at-padding-footer-right: @at-space-4x;
@at-margin-input-message: @at-space;
@at-margin-item-column: @at-space-3x;
@ -187,6 +205,9 @@
@at-margin-tag: @at-space-2x;
@at-margin-form-label: @at-space;
@at-margin-form-label-hint: @at-space-2x;
@at-margin-top-nav-item-between-icon-and-name: @at-space-2x;
@at-margin-top-nav-item-icon-socket-top-makeup: -3px;
@at-margin-after-footer-link: @at-space;
@at-margin-top-search-key: @at-space-2x;
@ -195,9 +216,17 @@
@at-height-textarea: 144px;
@at-height-button: 30px;
@at-height-tab: 30px;
@at-height-top-nav: 60px;
@at-height-top-nav-item-icon: 21px;
@at-height-top-nav-item-icon-socket: 18px;
@at-height-side-nav-item-icon: 20px;
@at-height-side-nav-spacer: 20px;
@at-height-top-side-nav-makeup: 55px;
@at-width-input-button-sm: 72px;
@at-width-input-button-md: 84px;
@at-width-collapsed-side-nav: 50px;
@at-width-expanded-side-nav: 200px;
// 4. Transitions ---------------------------------------------------------------------------------
@ -210,3 +239,7 @@
@at-line-height-short: 0.9;
@at-line-height-tall: 2;
@at-line-height: 24px;
@at-highlight-left-border-size: 5px;
@at-highlight-left-border-margin-makeup: -5px;
@at-z-index-nav: 1040;
@at-border-default-width: 1px;

View File

@ -19,7 +19,6 @@
* stuff, we'd be better off managing them via npm where possible.
*/
@import '../../legacy-styles/fonts';
@import '../../legacy-styles/main-layout';
@import '../../legacy-styles/animations';
@import '../../legacy-styles/jquery-ui-overrides';
@import '../../legacy-styles/codemirror';
@ -29,12 +28,10 @@
@import '../../legacy-styles/job-details';
@import '../../legacy-styles/jobs';
@import '../../legacy-styles/inventory-edit';
@import '../../legacy-styles/breadcrumbs';
@import '../../legacy-styles/stdout';
@import '../../legacy-styles/lists';
@import '../../legacy-styles/forms';
@import '../../legacy-styles/dashboard';
@import '../../legacy-styles/jPushMenu';
@import '../../legacy-styles/survey-maker';
@import '../../legacy-styles/text-label';
@import '../../legacy-styles/bootstrap-datepicker';
@ -70,7 +67,6 @@
@import '../../src/bread-crumb/bread-crumb.block.less';
@import '../../src/configuration/configuration.block.less';
@import '../../src/credentials/ownerList.block.less';
@import '../../src/footer/footer.block.less';
@import '../../src/home/dashboard/counts/dashboard-counts.block.less';
@import '../../src/home/dashboard/graphs/dashboard-graphs.block.less';
@import '../../src/home/dashboard/lists/dashboard-list.block.less';
@ -93,7 +89,6 @@
@import '../../src/login/loginModal/thirdPartySignOn/thirdPartySignOn.block.less';
@import '../../src/login/loginModal/loginModal.block.less';
@import '../../src/login/loginModal/loginModalNotice.block.less';
@import '../../src/main-menu/main-menu.block.less';
@import '../../src/management-jobs/card/mgmtcards.block.less';
@import '../../src/notifications/notifications.block.less';
@import '../../src/organizations/linkout/addUsers/addUsers.block.less';
@ -105,10 +100,6 @@
@import '../../src/scheduler/schedulertime.block.less';
@import '../../src/scheduler/scheduleToggle.block.less';
@import '../../src/scheduler/spinnerInput.block.less';
@import '../../src/setup-menu/hover-icon.block.less';
@import '../../src/setup-menu/setup-extra.block.less';
@import '../../src/setup-menu/setup-item.block.less';
@import '../../src/setup-menu/setup-menu.block.less';
@import '../../src/shared/container/container.block.less';
@import '../../src/shared/detail-nav/detail-nav.block.less';
@import '../../src/shared/icon/icon.block.less';

View File

@ -10,7 +10,7 @@ export default ['$rootScope', '$scope', '$state', 'ConfigService',
$('#about-modal').modal('show');
});
$('#about-modal').on('hidden.bs.modal', () => $state.go('setup'));
$('#about-modal').on('hidden.bs.modal', () => $state.go('dashboard'));
function createSpeechBubble (brand, version) {
let text = `${brand} ${version}`;

View File

@ -3,7 +3,7 @@ import controller from './about.controller';
import { N_ } from '../i18n';
export default {
name: 'setup.about',
name: 'about',
route: '/about',
controller: controller,
ncyBreadcrumb: {

View File

@ -51,8 +51,6 @@ import jobSubmission from './job-submission/main';
import notifications from './notifications/main';
import about from './about/main';
import license from './license/main';
import setupMenu from './setup-menu/main';
import mainMenu from './main-menu/main';
import breadCrumb from './bread-crumb/main';
import browserData from './browser-data/main';
import configuration from './configuration/main';
@ -67,7 +65,6 @@ import users from './users/main';
import projects from './projects/main';
import RestServices from './rest/main';
import access from './access/main';
import footer from './footer/main';
import scheduler from './scheduler/main';
import instanceGroups from './instance-groups/main';
@ -108,13 +105,10 @@ var awApp = angular.module('awApp', [
credentialTypes.name,
organizations.name,
managementJobs.name,
setupMenu.name,
mainMenu.name,
breadCrumb.name,
home.name,
login.name,
activityStream.name,
footer.name,
workflowResults.name,
jobResults.name,
jobSubmission.name,
@ -243,18 +237,6 @@ var awApp = angular.module('awApp', [
//base.replace(/\_/g, ' ');
base = (base === 'job_events' || base === 'job_host_summaries') ? 'jobs' : base;
}
$('#ansible-list-title').html('<strong>' + base.replace(/\_/, ' ') + '</strong>');
$('#ansible-main-menu li').each(function() {
$(this).removeClass('active');
});
$('#ansible-main-menu #' + base).addClass('active');
// Apply to mobile menu as well
$('#ansible-mobile-menu a').each(function() {
$(this).removeClass('active');
});
$('#ansible-mobile-menu a[href="#' + base + '"]').addClass('active');
}
if ($rootScope.removeConfigReady) {

View File

@ -6,10 +6,7 @@
background-color: @bc-bg;
width: 100%;
z-index: 1039;
position: fixed;
right: 0;
left: 0;
top: 60px;
margin-top: 60px;
height: 40px;
border-bottom: 1px solid @bc-border;
}

View File

@ -26,7 +26,6 @@
},
ncyBreadcrumb: {
parent: 'setup',
label: N_("EDIT CONFIGURATION")
},
controller: ConfigurationController,

View File

@ -41,7 +41,6 @@ angular.module('credentialTypes', [
activityStreamTarget: 'credential_type'
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('CREDENTIAL TYPES')
}
})

View File

@ -1,52 +0,0 @@
/** @define DashboardCounts */
.Footer {
height: 40px;
background-color: @default-secondary-bg;
color: @default-interface-txt;
width: 100%;
z-index: 1040;
position: absolute;
right: 0;
left: 0;
bottom: 0;
}
.Footer-logo {
float: left;
height: 40px;
width: 250px;
}
.Footer-logoImage {
height: 40px;
width: 250px;
}
.Footer-copyright{
float: right;
font-size: 12px;
margin-right: 20px;
margin-top: 10px;
}
.Footer-link:hover{
color: @default-bg;
}
.Footer-link{
color: @default-interface-txt;
}
.Footer-copyright.is-loggedOut,
.Footer-logo.is-loggedOut {
opacity: 0;
}
@menu-breakpoint: 553px;
@media screen and (max-width: (@menu-breakpoint)) {
.Footer-copyright{
display: none;
}
}

View File

@ -1,16 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
['templateUrl',
function(templateUrl) {
return {
restrict: 'E',
scope: true,
templateUrl: templateUrl('footer/footer')
};
}
];

View File

@ -1,3 +0,0 @@
<footer class='Footer'>
<div class="Footer-copyright Copyright-text" ng-class="{'is-loggedOut' : !current_user || !current_user.username}">Copyright &copy 2017 <a class="Footer-link" href="http://www.redhat.com" target="_blank">Red Hat</a>, Inc.</div>
</footer>

View File

@ -1,11 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import footerDirective from './footer.directive';
export default
angular.module('footer', [])
.directive('customFooter', footerDirective);

View File

@ -6,7 +6,6 @@ export default {
url: '/instance_groups',
searchPrefix: 'instance_group',
ncyBreadcrumb: {
parent: 'setup',
label: N_('INSTANCE GROUPS')
},
params: {

View File

@ -62,7 +62,6 @@ angular.module('inventoryScripts', [
activityStreamTarget: 'custom_inventory_script'
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('INVENTORY SCRIPTS')
}
})

View File

@ -15,7 +15,6 @@ export default {
controller: 'licenseController',
data: {},
ncyBreadcrumb: {
parent: 'setup',
label: N_('LICENSE')
},
onEnter: ['$state', 'ConfigService', (state, configService) => {

View File

@ -1,306 +0,0 @@
/** @define MainMenu */
.MainMenu {
padding: 0;
display: flex;
background-color: @menu-bg;
border-bottom: 1px solid @menu-btm;
width: 100%;
z-index: 1040;
position: fixed;
right: 0;
left: 0;
top: 0;
height: 60px;
align-items: stretch;
}
.MainMenu--licenseMissing{
justify-content: space-between;
}
.MainMenu-logo,
.MainMenu-item {
color: @menu-link;
background-color: @menu-link-bg;
}
.MainMenu-logoImage {
max-width: @main-menu-max-width;
max-height: @main-menu-max-height;
height: @main-menu-height;
width: @main-menu-width;
margin: @main-menu-margin;
}
.MainMenu-logoImage--licenseMissing:hover{
cursor:default;
background-color: @default-bg!important;
}
.MainMenu-item {
padding: 0 20px;
}
.MainMenu-itemText,
.MainMenu-itemImage {
flex: initial;
}
.MainMenu-itemImage {
color: @menu-link-icon;
}
.MainMenu-socket {
background-color: @default-bg;
order: 0;
flex: initial;
padding-top: 0px;
padding-left: 20px;
padding-right: 20px;
display: flex;
align-items: center;
border: 0;
border-left: 1px solid @menu-link-sides;
cursor: default;
}
.MainMenu-socketImage {
font-size: 18px;
text-shadow:
-1px -1px 0 @default-bg,
1px -1px 0 @default-bg,
-1px 1px 0 @default-bg,
1px 1px 0 @default-bg;
z-index: 1042;
}
.MainMenu-item,
.MainMenu-item:hover,
.MainMenu-item:focus,
.MainMenu-item:active {
color: @default-interface-txt;
}
@menu-breakpoint: 900px;
@media screen and (min-width: (@menu-breakpoint + 1px)) {
.MainMenu-mobileItems,
.MainMenu-toggle {
display: none;
}
.MainMenu-logo {
flex: initial;
padding: 0;
display: flex;
align-items: center;
border: 0;
border-bottom: 0;
border-right: 1px solid @menu-link-sides;
}
.MainMenu-item {
flex: initial;
padding-top: 0px;
border: 0;
border-bottom: 0px solid @menu-link-btm-hov;
display: flex;
align-items: center;
}
// Set up elements based on if their layout in the menu bar
.MainMenu-item--left {
border-right: 1px solid @menu-link-sides;
}
.MainMenu-item--left:hover,
.MainMenu-item--left.is-currentRoute {
margin-left: -1px;
padding-left: 21px;
padding-right: 21px;
border-right: 0px;
}
.MainMenu-item--lastLeft {
margin-right: auto;
}
.MainMenu-item--right {
border-left: 1px solid @menu-link-sides;
order: 1;
}
.MainMenu-item--right:hover,
.MainMenu-item--right.is-currentRoute {
border-left: 0px;
padding-left: 21px;
padding-right: 21px;
margin-right: -1px;
}
.MainMenu-item:hover,
.MainMenu-item.is-currentRoute {
padding-top: 5px;
border-bottom: 5px solid @menu-link-btm-hov;
}
.MainMenu-itemText--username {
padding-left: 13px;
margin-top: -4px;
max-width: 85px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.MainMenu-itemImage {
font-size: 20px;
}
.MainMenu-itemImage--settings {
margin-top: -1px;
}
.MainMenu-itemImage--user {
font-size: 21px;
margin-right: -5px;
margin-top: -1px;
}
.MainMenu-itemImage--docs {
font-size: 21px;
margin-top: -2px;
}
.MainMenu-item.is-currentRoute {
background-color: @menu-link-bg-hov;
}
}
@media screen and (max-width: @menu-breakpoint) {
.MainMenu-logo {
height: 60px;
}
.MainMenu-logoImage {
margin-top: @main-menu-margin-top-breakpoint;
}
.MainMenu-item--notMobile {
display: none;
}
.MainMenu-item--licenseMissing{
justify-content: space-between!important;
display:flex!important;
align-items: center!important;
flex: initial!important;
padding-top:0px;
}
.MainMenu-item--licenseMissing:hover{
padding-top:20px;
border-bottom: 5px solid @menu-link-btm-hov;
}
.MainMenu-itemImage--licenseMissing{
font-size: 20px!important;
}
.MainMenu {
flex-direction: row;
flex-wrap: wrap;
}
.MainMenu-logo {
flex: 1;
border-bottom: 1px solid @menu-link-btm;
order: 0;
}
.MainMenu-mobileItems {
display: flex;
order: 2;
flex-direction: row;
flex-wrap: wrap;
font-size: 15px;
width: 100%;
visibility: visible;
opacity: 1;
height: 416px;
}
.MainMenu-mobileItems.is-hiddenOnMobile,
.MainMenu-mobileItems.is-loggedOut {
visibility: hidden;
opacity: 0;
height: 0;
}
.MainMenu-item {
flex: 0 0 100%;
padding-top: 15px;
padding-bottom: 15px;
order: 1;
border-bottom: 1px solid @menu-link-btm;
}
.MainMenu-item.is-currentRoute {
font-weight: bold;
background-color: @menu-link-bg;
}
.MainMenu-item:hover {
background-color: @menu-link-bg-hov;
}
.MainMenu-toggle {
color: @menu-link-icon;
background-color: @menu-link-bg;
order: 0;
flex: initial;
padding-top: 3px;
padding-left: 15px;
padding-right: 15px;
font-size: 34px;
display: flex;
align-items: center;
border: 0;
border-left: 1px solid @menu-link-btm;
border-bottom: 1px solid @menu-link-btm;
}
.MainMenu-socket {
border-bottom: 1px solid @menu-link-btm;
}
.MainMenu-toggleImage {
width: 36px;
}
}
.MainMenu-toggle:focus,
.MainMenu-item:focus,
.MainMenu-logo:focus,
.MainMenu-toggle:hover,
.MainMenu-item:hover,
.MainMenu-logo:hover,
.MainMenu-item.is-currentRoute {
background-color: @default-tertiary-bg;
}
// item on
.MainMenu-logo,
.MainMenu-item,
.MainMenu-socket,
.MainMenu-toggle {
opacity: 1;
}
.MainMenu-logo.is-loggedOut,
.MainMenu-item.is-loggedOut,
.MainMenu-socket.is-loggedOut,
.MainMenu-toggle.is-loggedOut {
opacity: 0;
}

View File

@ -1,56 +0,0 @@
/* jshint unused: vars */
export default
[ '$state', 'templateUrl', '$rootScope', function($state, templateUrl, $rootScope) {
return {
restrict: 'E',
templateUrl: templateUrl('main-menu/main-menu'),
link: function(scope, element, attrs) {
scope.isCurrentState = function(name){
return $state.current.name === name;
};
scope.includesCurrentState = function(name){
return $state.includes(name);
};
// set up the user tooltip
$rootScope.$on('current_user', function(user) {
scope.currentUserTip = "Logged in as " + user.un;
});
// set up things for the socket notification
scope.socketHelp = $rootScope.socketHelp;
scope.socketTip = $rootScope.socketTip;
$rootScope.$watch('socketStatus', function(newStatus) {
scope.socketStatus = newStatus;
scope.socketIconClass = "icon-socket-" + scope.socketStatus;
});
$rootScope.$watch('socketTip', function(newTip) {
scope.socketTip = newTip;
});
// default the mobile menu as hidden
scope.isHiddenOnMobile = true;
// set up the click function to toggle mobile menu
scope.toggleMenu = function() {
if (scope.isHiddenOnMobile) {
scope.isHiddenOnMobile = false;
} else {
scope.isHiddenOnMobile = true;
}
};
// if the user clicks outside of the mobile menu,
// close it if it's open
$("body").on('click', function(e) {
if ($(e.target).parents(".MainMenu").length === 0) {
scope.isHiddenOnMobile = true;
}
});
// close the menu when the user clicks a link to a different route
scope.$on('$locationChangeStart', function(event) {
scope.isHiddenOnMobile = true;
});
}
};
}];

View File

@ -1,225 +0,0 @@
<nav id="main_menu" class="MainMenu" ng-class="{'MainMenu--licenseMissing' : licenseMissing}">
<!-- Menu logo item -->
<a id="main_menu_logo"
href="/#/"
class="MainMenu-logo ng-cloak"
ng-class="{'is-loggedOut' : !current_user || !current_user.username, 'MainMenu-logoImage--licenseMissing': licenseMissing}">
<img class="MainMenu-logoImage"
ng-class="{'MainMenu-logoImage--licenseMissing': licenseMissing}"
ng-src="/static/assets/logo-header.svg">
</a>
<!-- Mobile menu items -->
<span id="main_menu_mobile_items" class="MainMenu-mobileItems" ng-class="{'is-hiddenOnMobile': isHiddenOnMobile, 'is-loggedOut' : !current_user || !current_user.username}">
<a class="MainMenu-item"
id="main_menu_projects_mobile_link"
href="/#/projects"
ng-class="{'is-currentRoute' : isCurrentState('projects')}">
<span class="MainMenu-itemText">
<translate>PROJECTS</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_inventories_mobile_link"
href="/#/inventories"
ng-class="{'is-currentRoute' : isCurrentState('inventories')}">
<span class="MainMenu-itemText">
<translate>INVENTORIES</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_job_templates_mobile_link"
href="/#/templates"
ng-class="{'is-currentRoute' : isCurrentState('templates')}">
<span class="MainMenu-itemText">
<translate>TEMPLATES</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_jobs_mobile_link"
href="/#/jobs"
ng-class="{'is-currentRoute' : isCurrentState('jobs')}">
<span class="MainMenu-itemText">
<translate>JOBS</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_current_user_mobile_link"
ng-href="/#/users/{{ $root.current_user.id }}"
ng-class="{'is-currentRoute' : isCurrentState('users.edit')}">
<span class="MainMenu-itemText">
<translate>VIEW USER PAGE FOR {{ $root.current_user.username | uppercase }}</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_setup_mobile_link"
ng-href="/#/setup"
ng-class="{'is-currentRoute' : isCurrentState('setup')}">
<span class="MainMenu-itemText">
<translate>SETTINGS</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_setup_mobile_link"
ui-sref="portalMode"
ng-class="{'is-currentRoute' : isCurrentState('portalMode')}">
<span class="MainMenu-itemText">
<translate>PORTAL MODE</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_docs_mobile_link"
ng-href="http://docs.ansible.com/ansible-tower/"
target="_blank">
<span class="MainMenu-itemText">
<translate>VIEW DOCUMENTATION</translate>
</span>
</a>
<a class="MainMenu-item"
id="main_menu_logout_mobile_link"
ng-href="/#/logout"
ng-class="{'is-currentRoute' : isCurrentState('logout')}">
<span class="MainMenu-itemText">
<translate>LOG OUT</translate>
</span>
</a>
</span>
<!-- Non-mobile menu items -->
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left ng-cloak"
id="main_menu_projects_link"
href="/#/projects"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('projects'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
<translate>PROJECTS</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left"
id="main_menu_inventories_link"
href="/#/inventories"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('inventories'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
<translate>INVENTORIES</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left"
id="main_menu_job_templates_link"
href="/#/templates"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('templates'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
<translate>TEMPLATES</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left MainMenu-item--lastLeft"
id="main_menu_jobs_link"
href="/#/jobs"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('jobs'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
<translate>JOBS</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--user MainMenu-item--right"
id="main_menu_current_user_link"
ng-href="/#/users/{{ $root.current_user.id }}"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('users.edit'), 'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="{{currentUserTip}}"
aw-tip-watch="currentUserTip"
data-placement="bottom"
data-trigger="hover"
data-container="body">
<i class="MainMenu-itemImage MainMenu-itemImage--user icon-user"
alt="Logged in as {{ $root.current_user.username}}">
</i>
<span class="MainMenu-itemText MainMenu-itemText--username">
{{ $root.current_user.username}}
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_setup_link"
ng-href="/#/setup"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('setup'), 'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="{{'Settings'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body">
<i class="MainMenu-itemImage MainMenu-itemImage--settings fa fa-cog"
alt="Settings">
</i>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_portal_link"
ng-href="/#/portal/myjobs"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('portalMode'), 'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="{{'My View'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body">
<i class="MainMenu-itemImage MainMenu-itemImage--settings fa fa-tasks"
alt="Portal Mode">
</i>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_docs_link"
ng-href="http://docs.ansible.com/ansible-tower/"
ng-hide="licenseMissing"
ng-class="{'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="{{'View Documentation'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body"
target="_blank">
<i class="MainMenu-itemImage MainMenu-itemImage--docs fa fa-book"
alt="View Documentation">
</i>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_logout_link"
ng-href="/#/logout"
ng-class="{'is-currentRoute' : isCurrentState('logout'),
'is-loggedOut' : !current_user || !current_user.username,
'MainMenu-item--licenseMissing' : licenseMissing}"
aw-tool-tip="{{'Log Out'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body"
data-tooltip_inner_class="tooltip-inner--logOut">
<i class="MainMenu-itemImage fa fa-power-off"
ng-class="{'MainMenu-itemImage--licenseMissing' : licenseMissing}"
alt="Log Out">
</i>
</a>
<!-- Socket-status item -->
<div
id="main_menu_socket_status_notification"
class="MainMenu-socket"
aw-tool-tip="{{socketTip}}"
tip-watch="socketTip"
data-placement="bottom"
data-trigger="hover"
data-container="body"
ng-class="{'is-loggedOut' : !current_user || !current_user.username}"
ng-hide="licenseMissing"
ng-if="socketStatus && socketStatus !== 'ok'">
<i class="fa MainMenu-socketImage"
ng-class="socketIconClass">
</i>
</div>
<!-- Mobile menu toggle item -->
<button
id="main_menu_mobile_toggle_button"
class="MainMenu-toggle"
ng-hide="licenseMissing"
ng-class="{'is-active': !isHiddenOnMobile, 'is-loggedOut' : !current_user || !current_user.username}"
ng-click="toggleMenu()">
<i class="fa fa-bars MainMenu-toggleImage"></i>
</button>
</nav>

View File

@ -1,5 +0,0 @@
import mainMenu from './main-menu.directive';
export default
angular.module('mainMenu', [])
.directive('mainMenu', mainMenu);

View File

@ -17,7 +17,6 @@ export default {
activityStreamTarget: 'job'
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('MANAGEMENT JOBS')
},
};

View File

@ -39,7 +39,6 @@ angular.module('notifications', [
name: 'notifications',
url: '/notification_templates',
ncyBreadcrumb: {
parent: 'setup',
label: N_("NOTIFICATIONS")
},
lazyLoad: () => stateDefinitions.generateTree({
@ -87,7 +86,6 @@ angular.module('notifications', [
activityStreamTarget: 'notification_template'
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('NOTIFICATIONS')
}
})

View File

@ -48,7 +48,6 @@ angular.module('Organizations', [
list: templateUrl('organizations/list/organizations-list')
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('ORGANIZATIONS')
},
data: {

View File

@ -1,45 +0,0 @@
/** @define HoverIcon */
.HoverIcon {
@media screen and (max-width: 571px) {
&--onlyLarge {
.HoverIcon-icon--opacity {
opacity: 1;
}
.HoverIcon-icon--height {
height: 22px;
}
}
}
&-icon {
width: auto;
&--color {
g, path {
transition: fill 0.15s ease-out;
}
}
&--opacity {
transition: opacity 0.3s ease-out;
opacity: 0;
}
&--height {
transition: height 0.2s ease-out;
height: 0;
}
}
&:hover {
.HoverIcon-icon {
&--opacity {
opacity: 1;
}
&--height {
height: 22px;
}
&--color {
path, g {
fill: @blue;
}
}
}
}
}

View File

@ -1,12 +0,0 @@
import route from './setup.route';
import icon from '../shared/icon/main';
export default
angular.module('setupMenu',
[ icon.name])
.run(['$stateExtender', 'I18NInit',
function($stateExtender, I18NInit) {
I18NInit();
$stateExtender.addState(route);
}]);

View File

@ -1,14 +0,0 @@
/** @define SetupExtra */
.SetupExtra {
&-itemTitle {
font-size: 1.4rem;
font-weight: bold;
}
&-itemDescription {
font-family: merriweather;
font-size: 1rem;
font-weight: lighter;
font-style: italic;
}
}

View File

@ -1,73 +0,0 @@
/** @define SetupItem */
.SetupItem {
background-color: @panel-bg;
border-radius: 5px;
border: 1px solid @b7grey;
min-height: 120px;
padding: 20px;
margin-top: 20px;
transition: background-color 0.2s;
cursor:pointer;
}
.SetupItem:hover {
background-color: @btn-bg-hov;
}
.SetupItem-title{
font-size: 14px;
color: @default-interface-txt;
text-align: center;
margin-bottom: 15px;
font-weight: 600;
text-transform: uppercase;
margin-top: 0;
}
.SetupItem-description{
font-size: 12px;
color: @default-interface-txt;
text-align: center;
margin-bottom: 0;
}
@media (min-width: 1179px) {
.SetupItem {
width: ~"calc(25% - 20px)";
margin-right: 20px;
}
.SetupItem :nth-child(4n+4) {
margin-right: 0px;
}
}
@media (min-width: 901px) and (max-width: 1178px) {
.SetupItem {
width: ~"calc(33% - 11px)";
margin-right: 20px;
}
.SetupItem:nth-child(3n+3) {
margin-right: 0px;
}
}
@media (min-width: 616px) and (max-width: 900px) {
.SetupItem {
width: ~"calc(50% - 10px)";
margin-right: 20px;
}
.SetupItem:nth-child(2n+2) {
margin-right: 0px;
}
}
@media (max-width: 615px) {
.SetupItem {
width: 100%;
margin-right: 0px;
}
}

View File

@ -1,13 +0,0 @@
/** @define SetupMenu */
@menu-breakpoint: 710px;
.SetupMenu {
width: 100%;
margin-left: 0;
display: flex;
flex-wrap: wrap;
@media screen and (max-width: @menu-breakpoint) {
margin-left: 0px;
}
}

View File

@ -1,79 +0,0 @@
<section id="htmlTemplate" class="Container">
<div class="SetupMenu">
<a ui-sref="organizations" class="SetupItem">
<h4 class="SetupItem-title " translate>Organizations</h4>
<p class="SetupItem-description" translate>
Group all of your content to manage permissions across departments in your company.
</p>
</a>
<a ui-sref="users" class="SetupItem">
<h4 class="SetupItem-title" translate>Users</h4>
<p class="SetupItem-description" translate>
Allow others to sign into {{BRAND_NAME}} and own the content they create.
</p>
</a>
<a ui-sref="teams" class="SetupItem">
<h4 class="SetupItem-title" translate>Teams</h4>
<p class="SetupItem-description" translate>
Split up your organization to associate content and control permissions for groups.
</p>
</a>
<a ui-sref="credentials" class="SetupItem">
<h4 class="SetupItem-title" translate>Credentials</h4>
<p class="SetupItem-description" translate>
Add passwords, SSH keys, and other credentials to use when launching jobs against machines, or when syncing inventories or projects.
</p>
</a>
<a ui-sref="credentialTypes" class="SetupItem"
ng-if="user_is_system_auditor || user_is_superuser">
<h4 class="SetupItem-title" translate>Credential Types</h4>
<p class="SetupItem-description" translate>
Create custom credential types to be used for authenticating to network hosts and cloud sources
</p>
</a>
<a ui-sref="managementJobsList" class="SetupItem" ng-if="user_is_superuser || user_is_system_auditor">
<h4 class="SetupItem-title" translate>Management Jobs</h4>
<p class="SetupItem-description" translate>
Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info.
</p>
</a>
<a ui-sref="inventoryScripts" class="SetupItem">
<h4 class="SetupItem-title" translate>Inventory Scripts</h4>
<p class="SetupItem-description" translate>
Create and edit scripts to dynamically load hosts from any source.
</p>
</a>
<a ui-sref="notifications" class="SetupItem"
ng-if="orgAdmin || user_is_system_auditor || user_is_superuser">
<h4 class="SetupItem-title" translate>Notifications</h4>
<p class="SetupItem-description" translate>
Create templates for sending notifications with Email, HipChat, Slack, and SMS.
</p>
</a>
<a ui-sref="instanceGroups" class="SetupItem">
<h4 class="SetupItem-title" translate>Instance Groups</h4>
<p class="SetupItem-description" translate>
View list and capacity of {{BRAND_NAME}} instances.
</p>
</a>
<a ui-sref="configuration" class="SetupItem" ng-if="user_is_superuser || user_is_system_auditor">
<h4 class="SetupItem-title" translate>Configure {{BRAND_NAME}}</h4>
<p class="SetupItem-description" translate>
Edit {{BRAND_NAME}}'s configuration.
</p>
</a>
<a ui-sref="setup.about" class="SetupItem">
<h4 class="SetupItem-title" translate>About {{BRAND_NAME}}</h4>
<p class="SetupItem-description" translate>
View information about this version of Ansible {{BRAND_NAME}}.
</p>
</a>
<a ui-sref="license" class="SetupItem" ng-if="!isOpen">
<h4 class="SetupItem-title" translate>View Your License</h4>
<p class="SetupItem-description" translate>
View and edit your license information.
</p>
</a>
</div>
<div ui-view></div>
</section>

View File

@ -1,48 +0,0 @@
import {templateUrl} from '../shared/template-url/template-url.factory';
import { N_ } from '../i18n';
import _ from 'lodash';
export default {
name: 'setup',
route: '/setup',
ncyBreadcrumb: {
label: N_("SETTINGS")
},
templateUrl: templateUrl('setup-menu/setup-menu'),
controller: function(config, orgAdmin, $scope){
$scope.isOpen = _.get(config, 'license_info.license_type') === 'open';
$scope.orgAdmin = orgAdmin;
},
resolve: {
config: ['ConfigService', config => config.getConfig()],
orgAdmin:
['$rootScope', 'ProcessErrors', 'Rest', 'GetBasePath',
function($rootScope, ProcessErrors, Rest, GetBasePath){
return $rootScope.loginConfig.promise.then(function () {
if($rootScope.current_user.related.admin_of_organizations){
$rootScope.orgAdmin = false;
if ($rootScope.current_user.is_superuser) {
return true;
} else {
Rest.setUrl(GetBasePath('users') + `${$rootScope.current_user.id}/admin_of_organizations`);
return Rest.get().then(function(data){
if(data.data.count){
return true;
}
else{
return false;
}
})
.catch(function (data, status) {
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', msg: 'Failed to find if users is admin of org' + status });
});
}
}
else{
return false;
}
});
}]
}
};

View File

@ -42,7 +42,6 @@ angular.module('Teams', [])
activityStreamTarget: 'team'
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('TEAMS')
}
})

View File

@ -42,7 +42,6 @@ angular.module('Users', [])
activityStreamTarget: 'user'
},
ncyBreadcrumb: {
parent: 'setup',
label: N_('USERS')
}
})

View File

@ -18,146 +18,146 @@
</head>
<body data-user-agent="{{userAgent}}">
<main-menu></main-menu>
<bread-crumb></bread-crumb>
<toast></toast>
<div class="container-fluid" id="content-container">
<div class="row">
<div class="col-lg-12" ui-view>
<at-layout>
<bread-crumb></bread-crumb>
<toast></toast>
<div class="container-fluid" id="content-container">
<div class="row">
<div class="col-lg-12" ui-view>
</div>
</div>
</div>
<!-- Password Dialog -->
<div id="password-modal" style="display: none;"></div>
<div id="idle-modal" style="display:none">{% blocktrans %}Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?{% endblocktrans %}</div>
<stream-detail-modal></stream-detail-modal>
<!-- Confirmation Dialog -->
<div id="prompt-modal" class="modal fade">
<div class="modal-dialog">
<div class="Modal-content modal-content">
<div class="Modal-header">
<div class="Modal-title" ng-bind="promptHeader" id="prompt-header"></div>
<div class="Modal-exitHolder">
<button class="close Modal-exit" data-target="#prompt-modal" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
<!-- Password Dialog -->
<div id="password-modal" style="display: none;"></div>
<div id="idle-modal" style="display:none">{% blocktrans %}Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?{% endblocktrans %}</div>
<stream-detail-modal></stream-detail-modal>
<!-- Confirmation Dialog -->
<div id="prompt-modal" class="modal fade">
<div class="modal-dialog">
<div class="Modal-content modal-content">
<div class="Modal-header">
<div class="Modal-title" ng-bind="promptHeader" id="prompt-header"></div>
<div class="Modal-exitHolder">
<button class="close Modal-exit" data-target="#prompt-modal" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
</div>
</div>
<div class="Modal-body" ng-bind-html="promptBody" id="prompt-body">
</div>
<div class="Modal-footer">
<a href="#" data-target="#prompt-modal" data-dismiss="modal" id="prompt_cancel_btn" class="btn Modal-defaultButton Modal-footerButton">{% trans 'CANCEL' %}</a>
<a href="" ng-class="promptActionBtnClass" ng-click="promptAction()" id="prompt_action_btn" class="btn Modal-footerButton" ng-bind="promptActionText"></a>
</div>
</div>
<div class="Modal-body" ng-bind-html="promptBody" id="prompt-body">
</div>
<div class="Modal-footer">
<a href="#" data-target="#prompt-modal" data-dismiss="modal" id="prompt_cancel_btn" class="btn Modal-defaultButton Modal-footerButton">{% trans 'CANCEL' %}</a>
<a href="" ng-class="promptActionBtnClass" ng-click="promptAction()" id="prompt_action_btn" class="btn Modal-footerButton" ng-bind="promptActionText"></a>
</div>
<!-- modal-content -->
</div>
<!-- modal-content -->
<!-- modal-dialog -->
</div>
<!-- modal-dialog -->
</div>
<!-- modal -->
<!-- Alerts/error handling dialogs -->
<div id="alert-modal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" ng-hide="disableButtons" data-target="#alert-modal" data-dismiss="modal" class="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
<h3 id="alertHeader" ng-bind="alertHeader"></h3>
</div>
<div class="modal-body">
<div id="alert-modal-msg" class="alert" ng-bind-html="alertBody"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" id="alert_ok_btn" class="btn btn-default">{% trans 'OK' %}</a>
<!-- modal -->
<!-- Alerts/error handling dialogs -->
<div id="alert-modal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" ng-hide="disableButtons" data-target="#alert-modal" data-dismiss="modal" class="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
<h3 id="alertHeader" ng-bind="alertHeader"></h3>
</div>
<div class="modal-body">
<div id="alert-modal-msg" class="alert" ng-bind-html="alertBody"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" id="alert_ok_btn" class="btn btn-default">{% trans 'OK' %}</a>
</div>
</div>
<!-- modal-content -->
</div>
<!-- modal-content -->
<!-- modal-dialog -->
</div>
<!-- modal-dialog -->
</div>
<!-- modal -->
<div id="alert-modal2" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-target="#alert-modal2" data-dismiss="modal" ng-hide="disableButtons2" aria-hidden="true">&times;</button>
<h3 id="alertHeader2" ng-bind="alertHeader2"></h3>
</div>
<div class="modal-body">
<div id="alert2-modal-msg" class="alert" ng-bind-html="alertBody2"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" id="alert2_ok_btn" class="btn btn-primary">{% trans 'OK' %}</a>
<!-- modal -->
<div id="alert-modal2" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-target="#alert-modal2" data-dismiss="modal" ng-hide="disableButtons2" aria-hidden="true">&times;</button>
<h3 id="alertHeader2" ng-bind="alertHeader2"></h3>
</div>
<div class="modal-body">
<div id="alert2-modal-msg" class="alert" ng-bind-html="alertBody2"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" id="alert2_ok_btn" class="btn btn-primary">{% trans 'OK' %}</a>
</div>
</div>
<!-- modal-content -->
</div>
<!-- modal-content -->
<!-- modal-dialog -->
</div>
<!-- modal-dialog -->
</div>
<!-- modal -->
<div id="login-modal-dialog" style="display: none;"></div>
<div id="help-modal-dialog" style="display: none;"></div>
<div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form" class="MgmtCards-promptText">
{% trans 'Set how many days of data should be retained.' %}
<br>
<input type="integer" id="days_to_keep" name="days_to_keep" ng-model="days_to_keep" ng-required="true" class="form-control Form-textInput" min=0 max=9999 style="margin-top:10px;" integer>
<div class="error" ng-show="prompt_for_days_form.days_to_keep.$dirty && (prompt_for_days_form.days_to_keep.$error.number || prompt_for_days_form.days_to_keep.$error.integer ||
prompt_for_days_form.days_to_keep.$error.required ||
prompt_for_days_form.days_to_keep.$error.min ||
prompt_for_days_form.days_to_keep.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</form>
</div>
<div id="prompt-for-days-facts" style="display:none">
<form name="prompt_for_days_facts_form" id="prompt_for_days_facts_form" class="MgmtCards-promptText">
<div style="padding-bottom:15px;">{% blocktrans %}For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept.
<!-- modal -->
<div id="login-modal-dialog" style="display: none;"></div>
<div id="help-modal-dialog" style="display: none;"></div>
<div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form" class="MgmtCards-promptText">
{% trans 'Set how many days of data should be retained.' %}
<br>
<br> CAUTION: Setting both numerical variables to "0" will delete all facts.
<br>
<br>{% endblocktrans %}
</div>
<div class="form-group">
<label for="description">
<span class="label-text">
{% trans 'Select a time period after which to remove old facts' %}
</span>
</label>
<div class="row">
<div class="col-xs-6">
<input type="integer" id="keep_amount" name="keep_amount" ng-model="keep_amount" ng-required="true" class="form-control Form-textInput MgmtCards-card--promptElements" min=0 max=9999 integer></input>
</div>
<div class="col-xs-6">
<select id="keep_unit" name="keep_unit" ng-model="keep_unit" ng-options="type.label for type in keep_unit_choices track by type.value" ng-required="true" class="form-control MgmtCards-card--promptElements"></select>
</div>
<input type="integer" id="days_to_keep" name="days_to_keep" ng-model="days_to_keep" ng-required="true" class="form-control Form-textInput" min=0 max=9999 style="margin-top:10px;" integer>
<div class="error" ng-show="prompt_for_days_form.days_to_keep.$dirty && (prompt_for_days_form.days_to_keep.$error.number || prompt_for_days_form.days_to_keep.$error.integer ||
prompt_for_days_form.days_to_keep.$error.required ||
prompt_for_days_form.days_to_keep.$error.min ||
prompt_for_days_form.days_to_keep.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</form>
</div>
<div id="prompt-for-days-facts" style="display:none">
<form name="prompt_for_days_facts_form" id="prompt_for_days_facts_form" class="MgmtCards-promptText">
<div style="padding-bottom:15px;">{% blocktrans %}For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept.
<br>
<br> CAUTION: Setting both numerical variables to "0" will delete all facts.
<br>
<br>{% endblocktrans %}
</div>
<div class="error" ng-show="prompt_for_days_facts_form.keep_amount.$dirty && (prompt_for_days_facts_form.keep_amount.$error.number || prompt_for_days_facts_form.keep_amount.$error.integer ||
prompt_for_days_facts_form.keep_amount.$error.required ||
prompt_for_days_facts_form.keep_amount.$error.min ||
prompt_for_days_facts_form.keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</div>
<div class="form-group ">
<label for="description">
<span class="label-text">
{% trans 'Select a frequency for snapshot retention' %}
</span>
</label>
<div class="row">
<div class="col-xs-6">
<input type="integer" id="granularity_keep_amount" name="granularity_keep_amount" ng-model="granularity_keep_amount" ng-required="true" class="form-control Form-textInput MgmtCards-card--promptElements" min=0 max=9999 integer></input>
</div>
<div class="col-xs-6">
<select id="granularity_keep_unit" name="granularity_keep_unit" ng-model="granularity_keep_unit" ng-options="type.label for type in granularity_keep_unit_choices track by type.value" ng-required="true" class="form-control MgmtCards-card--promptElements"></select>
<div class="form-group">
<label for="description">
<span class="label-text">
{% trans 'Select a time period after which to remove old facts' %}
</span>
</label>
<div class="row">
<div class="col-xs-6">
<input type="integer" id="keep_amount" name="keep_amount" ng-model="keep_amount" ng-required="true" class="form-control Form-textInput MgmtCards-card--promptElements" min=0 max=9999 integer></input>
</div>
<div class="col-xs-6">
<select id="keep_unit" name="keep_unit" ng-model="keep_unit" ng-options="type.label for type in keep_unit_choices track by type.value" ng-required="true" class="form-control MgmtCards-card--promptElements"></select>
</div>
</div>
<div class="error" ng-show="prompt_for_days_facts_form.keep_amount.$dirty && (prompt_for_days_facts_form.keep_amount.$error.number || prompt_for_days_facts_form.keep_amount.$error.integer ||
prompt_for_days_facts_form.keep_amount.$error.required ||
prompt_for_days_facts_form.keep_amount.$error.min ||
prompt_for_days_facts_form.keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</div>
<div class="error" ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && (prompt_for_days_facts_form.granularity_keep_amount.$error.number || prompt_for_days_facts_form.granularity_keep_amount.$error.integer ||
prompt_for_days_facts_form.granularity_keep_amount.$error.required ||
prompt_for_days_facts_form.granularity_keep_amount.$error.min ||
prompt_for_days_facts_form.granularity_keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</div>
</form>
<div class="form-group ">
<label for="description">
<span class="label-text">
{% trans 'Select a frequency for snapshot retention' %}
</span>
</label>
<div class="row">
<div class="col-xs-6">
<input type="integer" id="granularity_keep_amount" name="granularity_keep_amount" ng-model="granularity_keep_amount" ng-required="true" class="form-control Form-textInput MgmtCards-card--promptElements" min=0 max=9999 integer></input>
</div>
<div class="col-xs-6">
<select id="granularity_keep_unit" name="granularity_keep_unit" ng-model="granularity_keep_unit" ng-options="type.label for type in granularity_keep_unit_choices track by type.value" ng-required="true" class="form-control MgmtCards-card--promptElements"></select>
</div>
</div>
<div class="error" ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && (prompt_for_days_facts_form.granularity_keep_amount.$error.number || prompt_for_days_facts_form.granularity_keep_amount.$error.integer ||
prompt_for_days_facts_form.granularity_keep_amount.$error.required ||
prompt_for_days_facts_form.granularity_keep_amount.$error.min ||
prompt_for_days_facts_form.granularity_keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</div>
</form>
</div>
<div class="overlay"></div>
<div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i>
<p>{% trans 'working...' %}</p>
</div>
</div>
<div class="overlay"></div>
<div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i>
<p>{% trans 'working...' %}</p>
</div>
</div>
<custom-footer></custom-footer>
</at-layout>
</body>
</html>