mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 18:09:57 -03:30
Add UI/UX polish in prep for merge
* Bring UI/UX inline with recent changes * Use select components as a stopgap for credential_types and orgs * Add tabs to permissions view * Add Organization model
This commit is contained in:
parent
861cfd3e13
commit
fa330db9c5
@ -1,11 +1,14 @@
|
||||
const DEFAULT_ORGANIZATION_PLACEHOLDER = 'SELECT AN ORGANIZATION';
|
||||
|
||||
function AddCredentialsController (models, $state) {
|
||||
let vm = this || {};
|
||||
|
||||
let me = models.me;
|
||||
let credential = models.credential;
|
||||
let credentialType = models.credentialType;
|
||||
let organization = models.organization;
|
||||
|
||||
vm.panelTitle = 'New Credential';
|
||||
vm.panelTitle = 'NEW CREDENTIAL';
|
||||
|
||||
vm.tab = {
|
||||
details: {
|
||||
@ -20,6 +23,13 @@ function AddCredentialsController (models, $state) {
|
||||
omit: ['user', 'team', 'inputs']
|
||||
});
|
||||
|
||||
vm.form.organization._placeholder = DEFAULT_ORGANIZATION_PLACEHOLDER;
|
||||
vm.form.organization._data = organization.get('results');
|
||||
vm.form.organization._format = 'objects';
|
||||
vm.form.organization._exp = 'org as org.name for org in state._data';
|
||||
vm.form.organization._display = 'name';
|
||||
vm.form.organization._key = 'id';
|
||||
|
||||
vm.form.credential_type._data = credentialType.get('results');
|
||||
vm.form.credential_type._placeholder = 'SELECT A TYPE';
|
||||
vm.form.credential_type._format = 'grouped-object';
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<at-form state="vm.form">
|
||||
<at-input-text col="4" tab="1" state="vm.form.name"></at-input-text>
|
||||
<at-input-text col="4" tab="2" state="vm.form.description"></at-input-text>
|
||||
<at-input-lookup col="4" tab="3" state="vm.form.organization"></at-input-lookup>
|
||||
<at-input-select col="4" tab="3" state="vm.form.organization"></at-input-select>
|
||||
|
||||
<at-divider></at-divider>
|
||||
|
||||
@ -32,6 +32,11 @@
|
||||
$state.current.name === 'credentials.edit.permissions.add'">
|
||||
<at-panel-heading>Credentials Permissions</at-panel-heading>
|
||||
|
||||
<at-tab-group>
|
||||
<at-tab state="vm.tab.details">Details</at-tab>
|
||||
<at-tab state="vm.tab.permissions">Permissions</at-tab>
|
||||
</at-tab-group>
|
||||
|
||||
<at-panel-body>
|
||||
<div class="at-CredentialsPermissions" ui-view="related"></div>
|
||||
</at-panel-body>
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
const DEFAULT_ORGANIZATION_PLACEHOLDER = 'SELECT AN ORGANIZATION';
|
||||
|
||||
function EditCredentialsController (models, $state, $scope) {
|
||||
let vm = this || {};
|
||||
|
||||
let me = models.me;
|
||||
let credential = models.credential;
|
||||
let credentialType = models.credentialType;
|
||||
let organization = models.organization;
|
||||
|
||||
vm.tab = {
|
||||
details: {
|
||||
_active: true
|
||||
_active: true,
|
||||
_go: 'credentials.edit',
|
||||
_params: { credential_id: credential.get('id') }
|
||||
},
|
||||
permissions:{
|
||||
_go: 'credentials.edit.permissions',
|
||||
@ -15,6 +20,16 @@ function EditCredentialsController (models, $state, $scope) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watch('$state.current.name', (value) => {
|
||||
if (value === 'credentials.edit') {
|
||||
vm.tab.details._active = true;
|
||||
vm.tab.details._permissions = false;
|
||||
} else {
|
||||
vm.tab.permissions._active = true;
|
||||
vm.tab.details._active = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Only exists for permissions compatibility
|
||||
$scope.credential_obj = credential.get();
|
||||
|
||||
@ -24,6 +39,14 @@ function EditCredentialsController (models, $state, $scope) {
|
||||
omit: ['user', 'team', 'inputs']
|
||||
});
|
||||
|
||||
vm.form.organization._placeholder = DEFAULT_ORGANIZATION_PLACEHOLDER;
|
||||
vm.form.organization._data = organization.get('results');
|
||||
vm.form.organization._format = 'objects';
|
||||
vm.form.organization._exp = 'org as org.name for org in state._data';
|
||||
vm.form.organization._display = 'name';
|
||||
vm.form.organization._key = 'id';
|
||||
vm.form.organization._value = organization.getById(credential.get('organization'));
|
||||
|
||||
vm.form.credential_type._data = credentialType.get('results');
|
||||
vm.form.credential_type._format = 'grouped-object';
|
||||
vm.form.credential_type._display = 'name';
|
||||
|
||||
@ -6,12 +6,13 @@ import AddController from './add-credentials.controller.js';
|
||||
import EditController from './edit-credentials.controller.js';
|
||||
import { N_ } from '../../src/i18n';
|
||||
|
||||
function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType) {
|
||||
function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization) {
|
||||
let id = $stateParams.credential_id;
|
||||
|
||||
let promises = {
|
||||
me: new Me('get'),
|
||||
credentialType: new CredentialType('get')
|
||||
credentialType: new CredentialType('get'),
|
||||
organization: new Organization('get')
|
||||
};
|
||||
|
||||
if (id) {
|
||||
@ -28,13 +29,13 @@ CredentialsResolve.$inject = [
|
||||
'$stateParams',
|
||||
'MeModel',
|
||||
'CredentialModel',
|
||||
'CredentialTypeModel'
|
||||
'CredentialTypeModel',
|
||||
'OrganizationModel'
|
||||
];
|
||||
|
||||
function CredentialsConfig ($stateProvider, $stateExtenderProvider, stateDefinitionsProvider, pathServiceProvider) {
|
||||
function CredentialsConfig ($stateProvider, $stateExtenderProvider, pathServiceProvider) {
|
||||
let pathService = pathServiceProvider.$get();
|
||||
let stateExtender = $stateExtenderProvider.$get();
|
||||
let stateDefinitions = stateDefinitionsProvider.$get();
|
||||
|
||||
stateExtender.addState({
|
||||
name: 'credentials',
|
||||
@ -272,7 +273,6 @@ function CredentialsConfig ($stateProvider, $stateExtenderProvider, stateDefinit
|
||||
CredentialsConfig.$inject = [
|
||||
'$stateProvider',
|
||||
'$stateExtenderProvider',
|
||||
'stateDefinitionsProvider',
|
||||
'PathServiceProvider'
|
||||
];
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
color: @at-gray-dark-5x;
|
||||
|
||||
&, &:active {
|
||||
border-color: @at-gray-dark-3x;
|
||||
border-color: @at-gray-dark-2x;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@ -19,25 +19,33 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
& > input[type=checkbox] {
|
||||
height: @at-input-height;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
& > label {
|
||||
& > input[type=checkbox] {
|
||||
height: @at-input-height;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& > p {
|
||||
margin: 0;
|
||||
padding: 0 0 0 @at-space-6x;
|
||||
line-height: @at-line-height-tall;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.at-InputContainer {
|
||||
margin-top: @at-space-6x;;
|
||||
margin-top: @at-space-6x;
|
||||
}
|
||||
|
||||
.at-Input-button {
|
||||
min-width: @at-input-button-width;
|
||||
display: block;
|
||||
height: 100%;
|
||||
height: @at-input-height;
|
||||
|
||||
&, &:active, &:hover, &:focus {
|
||||
color: @at-gray-dark-3x;
|
||||
border-color: @at-gray-dark-3x;
|
||||
border-color: @at-gray-dark-2x;
|
||||
background-color: @at-white;
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -76,12 +84,16 @@
|
||||
position: absolute;
|
||||
width: @at-inset-width;
|
||||
height: 100%;
|
||||
background: @at-gray;
|
||||
background: @at-gray-dark;
|
||||
left: -@at-inset-width;
|
||||
}
|
||||
|
||||
.at-InputGroup-button {
|
||||
height: 100%;
|
||||
|
||||
& > button {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.at-InputGroup-title {
|
||||
|
||||
@ -84,12 +84,14 @@ function BaseInputController () {
|
||||
scope.state._enableToggle = true;
|
||||
scope.state._value = scope.state._preEditValue;
|
||||
scope.state._activeModel = '_displayValue';
|
||||
scope.state._placeholder = 'ENCRYPTED';
|
||||
} else {
|
||||
scope.state._buttonText = 'REVERT';
|
||||
scope.state._disabled = false;
|
||||
scope.state._enableToggle = false;
|
||||
scope.state._activeModel = '_value';
|
||||
scope.state._value = '';
|
||||
scope.state._placeholder = '';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -14,6 +14,8 @@ function AtInputCheckboxController (baseInputController) {
|
||||
|
||||
vm.init = (scope, element, form) => {
|
||||
baseInputController.call(vm, 'input', scope, element, form);
|
||||
scope.label = scope.state.label;
|
||||
scope.state.label = 'OPTIONS';
|
||||
|
||||
vm.check();
|
||||
};
|
||||
|
||||
@ -2,12 +2,15 @@
|
||||
<div class="form-group at-u-flat">
|
||||
<at-input-label></at-input-label>
|
||||
<div class="checkbox at-InputCheckbox">
|
||||
<input type="checkbox"
|
||||
ng-class="{ 'at-Input--rejected': state.rejected }"
|
||||
ng-model="state._value"
|
||||
ng-attr-tabindex="{{ tab || undefined }}"
|
||||
ng-change="vm.check()"
|
||||
ng-disabled="state._disabled || form.disabled" />
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
ng-class="{ 'at-Input--rejected': state.rejected }"
|
||||
ng-model="state._value"
|
||||
ng-attr-tabindex="{{ tab || undefined }}"
|
||||
ng-change="vm.check()"
|
||||
ng-disabled="state._disabled || form.disabled" />
|
||||
<p>{{ label }}</p>
|
||||
</label>
|
||||
</div>
|
||||
<at-input-message></at-input-message>
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<input type="checkbox"
|
||||
ng-model="state._promptOnLaunch"
|
||||
ng-change="vm.togglePromptOnLaunch()" />
|
||||
<p>Prompt on launch?</p>
|
||||
<p>Prompt on launch</p>
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@ -26,6 +26,7 @@ function AtInputSecretController (baseInputController) {
|
||||
vm.toggle = vm.toggleShowHide;
|
||||
} else {
|
||||
scope.state._buttonText = 'REPLACE';
|
||||
scope.state._placeholder = 'ENCRYPTED';
|
||||
vm.toggle = vm.toggleRevertReplace;
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<at-input-label></at-input-label>
|
||||
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<span class="input-group-btn at-InputGroup-button">
|
||||
<button class="btn at-ButtonHollow--white at-Input-button"
|
||||
ng-disabled="!state._enableToggle && (state._disabled || form.disabled)"
|
||||
ng-click="vm.toggle()">
|
||||
@ -16,7 +16,7 @@
|
||||
ng-class="{ 'at-Input--rejected': state._rejected }"
|
||||
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
||||
ng-attr-tabindex="{{ tab || undefined }}"
|
||||
ng-attr-placeholder="{{::state._placeholder || undefined }}"
|
||||
ng-attr-placeholder="{{state._placeholder || undefined }}"
|
||||
ng-change="vm.check()"
|
||||
ng-disabled="state._disabled || form.disabled" />
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
const DEFAULT_EMPTY_PLACEHOLDER = 'NO OPTIONS AVAILABLE';
|
||||
|
||||
function atInputSelectLink (scope, element, attrs, controllers) {
|
||||
let formController = controllers[0];
|
||||
let inputController = controllers[1];
|
||||
@ -25,6 +27,11 @@ function AtInputSelectController (baseInputController, eventService) {
|
||||
input = element.find('input')[0];
|
||||
select = element.find('select')[0];
|
||||
|
||||
if (!scope.state._data || scope.state._data.length === 0) {
|
||||
scope.state._disabled = true;
|
||||
scope.state._placeholder = DEFAULT_EMPTY_PLACEHOLDER;
|
||||
}
|
||||
|
||||
vm.setListeners();
|
||||
vm.check();
|
||||
|
||||
@ -55,6 +62,8 @@ function AtInputSelectController (baseInputController, eventService) {
|
||||
vm.updateDisplayModel = () => {
|
||||
if (scope.state._format === 'array') {
|
||||
scope.displayModel = scope.state._data[scope.state._value];
|
||||
} else if (scope.state._format === 'objects') {
|
||||
scope.displayModel = scope.state._value[scope.state._display];
|
||||
} else if (scope.state._format === 'grouped-object') {
|
||||
scope.displayModel = scope.state._value[scope.state._display];
|
||||
} else {
|
||||
|
||||
@ -34,6 +34,7 @@ function AtInputTextareaSecretController (baseInputController, eventService) {
|
||||
|
||||
if (scope.state._value) {
|
||||
scope.state._buttonText = 'REPLACE';
|
||||
scope.state._placeholder = 'ENCRYPTED';
|
||||
} else {
|
||||
if (scope.state.format === 'ssh_private_key') {
|
||||
vm.listeners = vm.setFileListeners(textarea, input);
|
||||
@ -48,10 +49,12 @@ function AtInputTextareaSecretController (baseInputController, eventService) {
|
||||
vm.toggleRevertReplace();
|
||||
|
||||
if (scope.state._isBeingReplaced) {
|
||||
scope.state._placeholder = '';
|
||||
scope.state._displayHint = true;
|
||||
vm.listeners = vm.setFileListeners(textarea, input);
|
||||
} else {
|
||||
scope.state._displayHint = false;
|
||||
scope.state._placeholder = 'ENCRYPTED';
|
||||
eventService.remove(vm.listeners);
|
||||
}
|
||||
};
|
||||
|
||||
@ -18,9 +18,10 @@
|
||||
<textarea class="form-control at-Input at-InputTextarea"
|
||||
ng-model="state[state._activeModel]"
|
||||
ng-class="{ 'at-Input--rejected': state._rejected }"
|
||||
ng-attr-rows="{{::state._rows || 6 }}"
|
||||
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
||||
ng-attr-tabindex="{{ tab || undefined }}"
|
||||
ng-attr-placeholder="{{::state._placeholder || undefined }}"
|
||||
ng-attr-placeholder="{{state._placeholder || undefined }}"
|
||||
ng-change="vm.check()"
|
||||
ng-disabled="state._disabled || form.disabled" />
|
||||
</textarea>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
<textarea class="form-control at-Input at-InputTextarea"
|
||||
ng-model="state._value"
|
||||
ng-class="{ 'at-Input--rejected': state._rejected }"
|
||||
ng-attr-rows="{{::state._rows || 6 }}"
|
||||
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
||||
ng-attr-tabindex="{{ tab || undefined }}"
|
||||
ng-attr-placeholder="{{::state._placeholder || undefined }}"
|
||||
|
||||
@ -21,4 +21,5 @@
|
||||
|
||||
.at-Panel-headingTitle {
|
||||
.at-mixin-Heading(@at-font-size-3x);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<div class="at-Panel-dismiss">
|
||||
<i class="fa fa-times fa-lg" ng-click="dismiss()"></i>
|
||||
<i class="fa fa-times-circle fa-lg" ng-click="dismiss()"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
.at-Popover-title {
|
||||
.at-mixin-Heading(@at-font-size-2x);
|
||||
.at-mixin-Heading(@at-font-size);
|
||||
color: @at-white;
|
||||
margin-bottom: @at-space-4x;
|
||||
}
|
||||
@ -47,5 +47,5 @@
|
||||
.at-Popover-text {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
font-size: @at-font-size;
|
||||
}
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
|
||||
.at-Tab {
|
||||
margin: 0 @at-space-5x 0 0;
|
||||
font-size: @at-font-size;
|
||||
}
|
||||
|
||||
.at-Tab--active {
|
||||
&, &:hover, &:active, &:focus {
|
||||
color: @at-white;
|
||||
background-color: @at-gray-dark-2x;
|
||||
border-color: @at-gray-dark-2x;
|
||||
background-color: @at-gray-dark-3x;
|
||||
border-color: @at-gray-dark-3x;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
@ -18,8 +19,8 @@
|
||||
.at-Tab--disabled {
|
||||
&, &:hover, &:active, &:focus {
|
||||
background-color: @at-white;
|
||||
color: @at-gray-dark-3x;
|
||||
border-color: @at-gray-dark-3x;
|
||||
color: @at-gray-dark-2x;
|
||||
border-color: @at-gray-dark-2x;
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@ -128,10 +128,17 @@ function normalizePath (resource) {
|
||||
return `${version}${resource}/`;
|
||||
}
|
||||
|
||||
function getById (id) {
|
||||
let item = this.get('results').filter(result => result.id === id);
|
||||
|
||||
return item ? item[0] : undefined;
|
||||
}
|
||||
|
||||
function BaseModel (path) {
|
||||
this.model = {};
|
||||
this.get = get;
|
||||
this.normalizePath = normalizePath;
|
||||
this.getById = getById;
|
||||
this.request = request;
|
||||
this.http = {
|
||||
get: httpGet.bind(this),
|
||||
|
||||
@ -26,18 +26,11 @@ function mergeInputProperties (type) {
|
||||
});
|
||||
}
|
||||
|
||||
function getById (id) {
|
||||
let type = this.get('results').filter(type => type.id === id);
|
||||
|
||||
return type ? type[0] : undefined;
|
||||
}
|
||||
|
||||
function CredentialTypeModel (method, id) {
|
||||
BaseModel.call(this, 'credential_types');
|
||||
|
||||
this.categorizeByKind = categorizeByKind.bind(this);
|
||||
this.mergeInputProperties = mergeInputProperties.bind(this);
|
||||
this.getById = getById.bind(this);
|
||||
|
||||
return this.request(method, id)
|
||||
.then(() => this);
|
||||
|
||||
18
awx/ui/client/lib/models/Organization.js
Normal file
18
awx/ui/client/lib/models/Organization.js
Normal file
@ -0,0 +1,18 @@
|
||||
let BaseModel;
|
||||
|
||||
function OrganizationModel (method) {
|
||||
BaseModel.call(this, 'organizations');
|
||||
|
||||
return this.request(method)
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
function OrganizationModelLoader (_BaseModel_) {
|
||||
BaseModel = _BaseModel_;
|
||||
|
||||
return OrganizationModel;
|
||||
}
|
||||
|
||||
OrganizationModelLoader.$inject = ['BaseModel'];
|
||||
|
||||
export default OrganizationModelLoader;
|
||||
@ -2,11 +2,13 @@ import Base from './Base';
|
||||
import Credential from './Credential';
|
||||
import CredentialType from './CredentialType';
|
||||
import Me from './Me';
|
||||
import Organization from './Organization';
|
||||
|
||||
angular
|
||||
.module('at.lib.models', [])
|
||||
.service('BaseModel', Base)
|
||||
.service('CredentialModel', Credential)
|
||||
.service('CredentialTypeModel', CredentialType)
|
||||
.service('MeModel', Me);
|
||||
.service('MeModel', Me)
|
||||
.service('OrganizationModel', Organization);
|
||||
|
||||
|
||||
@ -10,6 +10,10 @@
|
||||
.at-Button--green {
|
||||
.at-mixin-Button();
|
||||
.at-mixin-ButtonColor('at-green', 'at-white');
|
||||
|
||||
&[disabled] {
|
||||
background: @at-gray-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.at-Button--blue {
|
||||
@ -25,6 +29,7 @@
|
||||
.at-ButtonHollow--white {
|
||||
.at-mixin-Button();
|
||||
.at-mixin-ButtonHollow('at-gray-dark-3x', 'at-gray-dark-2x');
|
||||
border-color: @at-gray-dark;
|
||||
}
|
||||
|
||||
.at-ButtonIcon {
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
.at-mixin-Button () {
|
||||
height: @at-input-height;
|
||||
padding: @at-space-2x @at-space-4x;
|
||||
font-size: @at-font-size;
|
||||
}
|
||||
|
||||
.at-mixin-ButtonColor (@background, @color, @hover: '@{background}--hover') {
|
||||
|
||||
@ -50,6 +50,7 @@
|
||||
@at-font-weight-3x: 900;
|
||||
|
||||
@at-line-height-short: 0.9;
|
||||
@at-line-height-tall: 2;
|
||||
@at-line-height: 24px;
|
||||
|
||||
// 3. Layout --------------------------------------------------------------------------------------
|
||||
@ -68,3 +69,4 @@
|
||||
@at-border-radius: 5px;
|
||||
@at-popover-width: 320px;
|
||||
@at-inset-width: 5px;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user