Merge pull request #3057 from kialam/add-list-toolbar

Expand and Collapse Jobs List

Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
softwarefactory-project-zuul[bot]
2019-01-24 16:45:57 +00:00
committed by GitHub
8 changed files with 195 additions and 87 deletions

View File

@@ -132,11 +132,11 @@ function ListJobsController (
let reloadListStateParams = null; let reloadListStateParams = null;
if (vm.jobs.length === 1 && $state.params.job_search && if (vm.jobs.length === 1 && $state.params.job_search &&
_.has($state, 'params.job_search.page') && _.has($state, 'params.job_search.page') &&
$state.params.job_search.page !== '1') { $state.params.job_search.page !== '1') {
reloadListStateParams = _.cloneDeep($state.params); reloadListStateParams = _.cloneDeep($state.params);
reloadListStateParams.job_search.page = reloadListStateParams.job_search.page =
(parseInt(reloadListStateParams.job_search.page, 10) - 1).toString(); (parseInt(reloadListStateParams.job_search.page, 10) - 1).toString();
} }
$state.go('.', reloadListStateParams, { reload: true }); $state.go('.', reloadListStateParams, { reload: true });
@@ -173,8 +173,8 @@ function ListJobsController (
let reloadListStateParams = null; let reloadListStateParams = null;
if (vm.jobs.length === 1 && $state.params.job_search && if (vm.jobs.length === 1 && $state.params.job_search &&
!_.isEmpty($state.params.job_search.page) && !_.isEmpty($state.params.job_search.page) &&
$state.params.job_search.page !== '1') { $state.params.job_search.page !== '1') {
const page = `${(parseInt(reloadListStateParams const page = `${(parseInt(reloadListStateParams
.job_search.page, 10) - 1)}`; .job_search.page, 10) - 1)}`;
reloadListStateParams = _.cloneDeep($state.params); reloadListStateParams = _.cloneDeep($state.params);
@@ -213,6 +213,16 @@ function ListJobsController (
vm.job_dataset = data; vm.job_dataset = data;
}); });
} }
vm.isCollapsed = true;
vm.onCollapse = () => {
vm.isCollapsed = true;
};
vm.onExpand = () => {
vm.isCollapsed = false;
};
} }
ListJobsController.$inject = [ ListJobsController.$inject = [

View File

@@ -13,86 +13,92 @@
search-bar-full-width="vm.isPortalMode"> search-bar-full-width="vm.isPortalMode">
</smart-search> </smart-search>
</div> </div>
<at-list-toolbar
ng-if="vm.jobs.length > 0"
sort-only="false"
on-collapse="vm.onCollapse"
on-expand="vm.onExpand"
is-collapsed="vm.isCollapsed">
</at-list-toolbar>
<at-list results="vm.jobs" empty-list-reason="{{ vm.emptyListReason }}"> <at-list results="vm.jobs" empty-list-reason="{{ vm.emptyListReason }}">
<!-- TODO: implement resources are missing red indicator as present in mockup --> <!-- TODO: implement resources are missing red indicator as present in mockup -->
<at-row ng-repeat="job in vm.jobs" id="job-{{ job.id }}"> <at-row ng-repeat="job in vm.jobs" id="job-{{ job.id }}" ng-class="vm.isCollapsed ? 'at-Row--collapsed' : ''">
<div class="at-Row-items"> <div class="at-Row-items">
<!-- TODO: include workflow tab as well --> <!-- TODO: include workflow tab as well -->
<at-row-item <div class="at-Row-container">
status="{{ job.status }}" <at-row-item
status-tip="{{ vm.strings.get('list.STATUS_TOOLTIP', job.status) }}" status="{{ job.status }}"
header-value="{{ job.id }} - {{ job.name }}" status-tip="{{ vm.strings.get('list.STATUS_TOOLTIP', job.status) }}"
header-state="{{ vm.getSref(job) }}" header-value="{{ job.id }} - {{ job.name }}"
header-tag="{{ vm.jobTypes[job.type] }}" header-state="{{ vm.getSref(job) }}"
secondary-tag="{{ vm.getSliceJobDetails(job) }}"> header-tag="{{ vm.jobTypes[job.type] }}"
</at-row-item> secondary-tag="{{ vm.getSliceJobDetails(job) }}">
<div class="at-Row--inline"> </at-row-item>
<div class="at-Row-actions">
<at-relaunch job="job" ng-show="job.summary_fields.user_capabilities.start">
</at-relaunch>
<at-row-action icon="fa-minus-circle" ng-click="vm.cancelJob(job)" tooltip="{{ vm.strings.get('listActions.CANCEL', vm.strings.get('list.JOB')) }}"
ng-show="!vm.isPortalMode && (job.summary_fields.user_capabilities.start &&
(job.status === 'pending' ||
job.status === 'waiting' ||
job.status === 'running')) || ($root.user_is_superuser && job.type === 'system_job' &&
(job.status === 'pending' ||
job.status === 'waiting' ||
job.status === 'running'))">
</at-row-action>
<at-row-action icon="fa-trash" ng-click="vm.deleteJob(job)" tooltip="{{ vm.strings.get('listActions.DELETE', vm.strings.get('list.JOB')) }}"
ng-show="!vm.isPortalMode && job.summary_fields.user_capabilities.delete &&
!(job.status === 'pending' ||
job.status === 'waiting' ||
job.status === 'running')">
</at-row-action>
</div>
</div>
<div class="at-Row-container--wrapped">
<at-row-item <at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_STARTED') }}" label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_STARTED') }}"
value="{{ job.started | longDate }}" value="{{ job.started | longDate }}">
inline="true">
</at-row-item> </at-row-item>
<at-row-item <at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_FINISHED') }}" label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_FINISHED') }}"
value="{{ job.finished | longDate }}" value="{{ job.finished | longDate }}">
inline="true"> </at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_LAUNCHED_BY') }}"
value="{{ job.summary_fields.created_by.username }}"
value-link="/#/users/{{ job.summary_fields.created_by.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_WORKFLOW_JOB') }}"
value="{{ job.summary_fields.source_workflow_job.name }}"
value-link="/#/workflows/{{ job.summary_fields.source_workflow_job.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_JOB_TEMPLATE') }}"
value="{{ job.summary_fields.job_template.name }}"
value-link="/#/templates/job_template/{{ job.summary_fields.job_template.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_INVENTORY') }}"
value="{{ job.summary_fields.inventory.name }}"
value-link="/#/inventories/{{job.summary_fields.inventory.kind === 'smart' ? 'smart' : 'inventory'}}/{{ job.summary_fields.inventory.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_PROJECT') }}"
value="{{ job.summary_fields.project.name }}"
value-link="/#/projects/{{ job.summary_fields.project.id }}">
</at-row-item> </at-row-item>
</div> </div>
<at-row-item <div class="at-Row-container--wrapped">
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_WORKFLOW_JOB') }}" <at-row-item
value="{{ job.summary_fields.source_workflow_job.name }}" label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_CREDENTIALS') }}"
value-link="/#/workflows/{{ job.summary_fields.source_workflow_job.id }}"> tag-values="job.summary_fields.credentials"
</at-row-item> tags-are-creds="true">
<at-row-item <at-toggle-tag ng-init="credTags=vm.buildCredentialTags(job.summary_fields.credentials)" tags="credTags"></at-toggle-tag>
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_LAUNCHED_BY') }}" </at-row-item>
value="{{ job.summary_fields.created_by.username }}" <labels-list class="LabelList" show-delete="false" is-row-item="true" state="job">
value-link="/#/users/{{ job.summary_fields.created_by.id }}"> </labels-list>
</at-row-item> </div>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_JOB_TEMPLATE') }}"
value="{{ job.summary_fields.job_template.name }}"
value-link="/#/templates/job_template/{{ job.summary_fields.job_template.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_INVENTORY') }}"
value="{{ job.summary_fields.inventory.name }}"
value-link="/#/inventories/{{job.summary_fields.inventory.kind === 'smart' ? 'smart' : 'inventory'}}/{{ job.summary_fields.inventory.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_PROJECT') }}"
value="{{ job.summary_fields.project.name }}"
value-link="/#/projects/{{ job.summary_fields.project.id }}">
</at-row-item>
<at-row-item
label-value="{{:: vm.strings.get('list.ROW_ITEM_LABEL_CREDENTIALS') }}"
tag-values="job.summary_fields.credentials"
tags-are-creds="true">
<at-toggle-tag ng-init="credTags=vm.buildCredentialTags(job.summary_fields.credentials)" tags="credTags"></at-toggle-tag>
</at-row-item>
<labels-list class="LabelList" show-delete="false" is-row-item="true" state="job">
</labels-list>
</div>
<div class="at-Row-actions">
<at-relaunch job="job"
ng-show="job.summary_fields.user_capabilities.start">
</at-relaunch>
<at-row-action icon="fa-minus-circle" ng-click="vm.cancelJob(job)"
tooltip="{{ vm.strings.get('listActions.CANCEL', vm.strings.get('list.JOB')) }}"
ng-show="!vm.isPortalMode && (job.summary_fields.user_capabilities.start &&
(job.status === 'pending' ||
job.status === 'waiting' ||
job.status === 'running')) || ($root.user_is_superuser && job.type === 'system_job' &&
(job.status === 'pending' ||
job.status === 'waiting' ||
job.status === 'running'))">
</at-row-action>
<at-row-action icon="fa-trash" ng-click="vm.deleteJob(job)"
tooltip="{{ vm.strings.get('listActions.DELETE', vm.strings.get('list.JOB')) }}"
ng-show="!vm.isPortalMode && job.summary_fields.user_capabilities.delete &&
!(job.status === 'pending' ||
job.status === 'waiting' ||
job.status === 'running')">
</at-row-action>
</div> </div>
</at-row> </at-row>
</at-list> </at-list>

View File

@@ -34,6 +34,7 @@ import tab from '~components/tabs/tab.directive';
import tabGroup from '~components/tabs/group.directive'; import tabGroup from '~components/tabs/group.directive';
import tag from '~components/tag/tag.directive'; import tag from '~components/tag/tag.directive';
import toggleTag from '~components/toggle-tag/toggle-tag.directive'; import toggleTag from '~components/toggle-tag/toggle-tag.directive';
import toolbar from '~components/list/list-toolbar.directive';
import topNavItem from '~components/layout/top-nav-item.directive'; import topNavItem from '~components/layout/top-nav-item.directive';
import truncate from '~components/truncate/truncate.directive'; import truncate from '~components/truncate/truncate.directive';
import atCodeMirror from '~components/code-mirror'; import atCodeMirror from '~components/code-mirror';
@@ -69,6 +70,7 @@ angular
.component('atLaunchTemplate', launchTemplate) .component('atLaunchTemplate', launchTemplate)
.directive('atLayout', layout) .directive('atLayout', layout)
.directive('atList', list) .directive('atList', list)
.directive('atListToolbar', toolbar)
.component('atRelaunch', relaunch) .component('atRelaunch', relaunch)
.directive('atRow', row) .directive('atRow', row)
.directive('atRowItem', rowItem) .directive('atRowItem', rowItem)

View File

@@ -58,16 +58,68 @@
right: 0; right: 0;
} }
.at-List-toolbar--attached {
display: flex;
justify-content: flex-end;
border: @at-border-default-width solid @at-color-list-border;
border-bottom: none;
border-radius: @at-border-radius;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.at-List-toolbar-item {
padding: 2px 10px;
border-left: 1px solid @at-color-list-border;
border-right: 1px solid @at-color-list-border;
&:hover {
cursor: pointer;
background: @at-gray-f2;
}
&:first-of-type {
border-right: none;
}
&:last-of-type {
border: none;
}
&:first-child {
border-left: 1px solid @at-color-list-border;
}
&.active {
background: @at-blue;
color: @at-white;
}
}
.at-List-container { .at-List-container {
border: @at-border-default-width solid @at-color-list-border; border: @at-border-default-width solid @at-color-list-border;
border-radius: @at-border-radius; border-radius: @at-border-radius;
} }
// Remove top left and right rounded corners of a list if there is a toolbar above it
.at-List-toolbar--attached + .at-List > .at-List-container {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.at-List-toolbar--attached + .at-List {
margin-top: 0;
}
.at-Row { .at-Row {
display: grid; display: grid;
grid-template-columns: 10px 1fr; grid-template-columns: 10px 1fr;
overflow: hidden;
} }
.at-Row--collapsed {
max-height: 50px !important;
}
.at-Row--active { .at-Row--active {
border-left: @at-border-style-list-active-indicator; border-left: @at-border-style-list-active-indicator;
border-top-left-radius: @at-border-radius; border-top-left-radius: @at-border-radius;
@@ -122,6 +174,7 @@
.at-Row-actions { .at-Row-actions {
display: flex; display: flex;
flex-wrap: wrap;
} }
.at-Row-items { .at-Row-items {
@@ -129,12 +182,12 @@
} }
.at-RowItem { .at-RowItem {
display: grid;
grid-template-columns: 120px 1fr;
align-items: center; align-items: center;
line-height: @at-height-list-row-item; line-height: @at-height-list-row-item;
word-wrap: break-word; word-wrap: break-word;
word-break: break-all; word-break: break-all;
display: block;
margin-right: 10px;
} }
.at-RowItem-status { .at-RowItem-status {
@@ -151,6 +204,16 @@
line-height: @at-line-height-list-row-item-header; line-height: @at-line-height-list-row-item-header;
} }
.at-Row-container {
display: flex;
justify-content: space-between;
}
.at-Row-container--wrapped {
display: flex;
flex-wrap: wrap;
}
.at-RowItem--isHeaderLink { .at-RowItem--isHeaderLink {
color: @at-blue; color: @at-blue;
cursor: pointer; cursor: pointer;
@@ -161,8 +224,7 @@
.at-RowItem--labels { .at-RowItem--labels {
line-height: @at-line-height-list-row-item-labels; line-height: @at-line-height-list-row-item-labels;
display: flex; display: inline-block;
flex-wrap: wrap;
* { * {
font-size: 10px; font-size: 10px;
@@ -174,8 +236,7 @@
} }
.at-RowItem-tagContainer { .at-RowItem-tagContainer {
display: flex; display: inline-block;
margin-left: @at-margin-left-list-row-item-tag-container;
flex-wrap: wrap; flex-wrap: wrap;
line-height: initial; line-height: initial;
} }
@@ -186,7 +247,7 @@
border-radius: @at-border-radius; border-radius: @at-border-radius;
color: @at-color-list-row-item-tag; color: @at-color-list-row-item-tag;
font-size: @at-font-size-list-row-item-tag; font-size: @at-font-size-list-row-item-tag;
margin: @at-space; margin: @at-space @at-space-4x;
padding: @at-padding-list-row-item-tag; padding: @at-padding-list-row-item-tag;
line-height: @at-line-height-list-row-item-tag; line-height: @at-line-height-list-row-item-tag;
word-break: keep-all; word-break: keep-all;
@@ -207,13 +268,17 @@
} }
.at-RowItem-label { .at-RowItem-label {
display: inline-block;
text-transform: uppercase; text-transform: uppercase;
color: @at-color-list-row-item-label; color: @at-color-list-row-item-label;
font-size: @at-font-size; font-size: @at-font-size;
margin-right: 10px;
} }
.at-RowItem-value { .at-RowItem-value {
display: inline-block;
font-size: @at-font-size-3x; font-size: @at-font-size-3x;
margin-right: 20px;
} }
.at-RowItem-badge { .at-RowItem-badge {

View File

@@ -0,0 +1,18 @@
const templateUrl = require('~components/list/list-toolbar.partial.html');
function atListToolbar () {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl,
scope: {
onExpand: '=',
onCollapse: '=',
sortOnly: '=',
isCollapsed: '=',
}
};
}
export default atListToolbar;

View File

@@ -0,0 +1,5 @@
<div class="at-List-toolbar--attached">
<div ng-class="isCollapsed ? 'active' : ''" ng-if="!sortOnly" class="at-List-toolbar-item" ng-click="onCollapse()">Compact</div>
<div ng-class="!isCollapsed ? 'active' : ''" ng-if="!sortOnly" class="at-List-toolbar-item" ng-click="onExpand()">Expanded</div>
<div class="at-List-toolbar-item">Sort</div>
</div>

View File

@@ -10,7 +10,11 @@
min-height: @at-space-4x; min-height: @at-space-4x;
overflow: hidden; overflow: hidden;
max-width: 200px; max-width: 200px;
margin: @at-space; margin: @at-space 0;
&:last-of-type {
margin-right: 20px;
}
} }
.TagComponent-name { .TagComponent-name {

View File

@@ -7,19 +7,16 @@
} }
.LabelList-tagContainer { .LabelList-tagContainer {
height: fit-content; display: inline-block;
} }
.LabelList-tagContainer, .LabelList-tagContainer {
.LabelList-seeMoreLess {
display: flex;
max-width: 200px; max-width: 200px;
} }
.LabelList-tag, .JobSubmission-previewTag { .LabelList-tag, .JobSubmission-previewTag {
border-radius: 5px; border-radius: 5px;
padding: 2px 10px; padding: 2px 10px;
margin: 3px 0px;
font-size: 12px; font-size: 12px;
color: @default-interface-txt; color: @default-interface-txt;
background-color: @default-list-header-bg; background-color: @default-list-header-bg;
@@ -36,6 +33,7 @@
cursor: pointer; cursor: pointer;
border-radius: 5px; border-radius: 5px;
font-size: 11px; font-size: 11px;
display: inherit;
} }
.LabelList-seeMoreLess:hover { .LabelList-seeMoreLess:hover {