Skip to content

Commit

Permalink
feat: new pipeline header component (#1085)
Browse files Browse the repository at this point in the history
  • Loading branch information
minghay authored Jul 11, 2024
1 parent 8d04c6f commit cde3c83
Show file tree
Hide file tree
Showing 8 changed files with 407 additions and 16 deletions.
99 changes: 99 additions & 0 deletions app/components/pipeline/header/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';
import { action } from '@ember/object';

export default class PipelineHeaderComponent extends Component {
@service scm;

@service shuttle;

@tracked addToCollectionModalOpen = false;

@tracked errorMessage;

@tracked collections = null;

sameRepoPipeline = [];

get sonarBadgeUri() {
return (
this.args.pipeline.badges.sonar.uri ||
this.args.pipeline.badges.sonar.defaultUri
);
}

get sonarBadgeDescription() {
const sonarBadgeDescription = 'SonarQube project';
const sonarBadgeName =
this.args.pipeline.badges.sonar.name ||
this.args.pipeline.badges.sonar.defaultName;

return sonarBadgeName
? `SonarQube project: ${sonarBadgeName}`
: sonarBadgeDescription;
}

get scmContext() {
const scm = this.scm.getScm(this.args.pipeline.scmContext);

return {
scm: scm.displayName,
scmIcon: scm.iconType
};
}

@action
async getPipelinesWithSameRepo() {
const pipelineId = this.args.pipeline.id;

if (this.args.pipeline.scmRepo && this.args.pipeline.scmUri) {
const [scm, repositoryId] = this.args.pipeline.scmUri.split(':');

this.sameRepoPipeline = await this.shuttle
.fetchFromApi(
'get',
`/pipelines?search=${this.args.pipeline.scmRepo.name}&sortBy=name&sort=ascending`
)
.then(pipelines =>
pipelines
.filter(pipeline => {
const [s, r] = pipeline.scmUri.split(':');

return (
pipeline.id !== pipelineId && scm === s && repositoryId === r
);
})
.map((pipeline, i) => ({
index: i,
url: `/pipelines/${pipeline.id}`,
branchAndRootDir: pipeline.scmRepo.rootDir
? `${pipeline.scmRepo.branch}:${pipeline.scmRepo.rootDir}`
: pipeline.scmRepo.branch
}))
.sort((l, r) =>
l.branchAndRootDir.localeCompare(r.branchAndRootDir)
)
);
}
}

@action
async openAddToCollectionModal() {
if (!this.collections) {
this.collections = await this.shuttle
.fetchFromApi('get', '/collections')
.catch(err => {
this.errorMessage = `Could not get collections. ${err.message}`;
});
}

this.addToCollectionModalOpen = true;
}

@action
closeAddToCollectionModal(collections) {
this.addToCollectionModalOpen = false;
this.collections = collections;
}
}
84 changes: 84 additions & 0 deletions app/components/pipeline/header/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@use 'screwdriver-colors' as colors;
@use 'screwdriver-button' as button;
@use 'variables';

@mixin styles {
#pipeline-header {
display: flex;
background-color: colors.$sd-flyout-bg;
padding: 1rem;

a {
color: colors.$sd-black;
}

.header-item {
margin-top: auto;
margin-bottom: auto;
padding-right: 0.5rem;

&.pipeline-name {
font-size: 24px;
font-weight: variables.$weight-bold;
}

.sonarqube {
height: 1rem;
width: 1rem;
}

&.dropdown {
max-width: 20rem;

.dropdown-toggle {
display: flex;
justify-content: space-between;

svg {
margin: auto 0.25rem;
}

&::after {
margin-top: auto;
margin-bottom: 0.75rem;
}

.branch {
overflow: auto;
}
}

.dropdown-menu {
display: flex;
flex-direction: column;
overflow-x: scroll;
max-height: 10rem;
width: 20rem;

svg {
width: 10px;
height: 10px;
}

a {
padding: 0.25rem 0.75rem;

&:hover {
background-color: colors.$sd-highlight;
}
}

span {
margin: auto;
}
}
}
}

@include button.styles;

#add-to-collection {
margin-left: auto;
}
}
}
90 changes: 90 additions & 0 deletions app/components/pipeline/header/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<div id="pipeline-header" {{did-insert this.getPipelinesWithSameRepo}}>
<LinkTo
id="pipeline-link"
class="header-item pipeline-name"
@route="v2.pipeline"
@model={{@pipeline.id}}
>
{{@pipeline.scmRepo.name}}
</LinkTo>

{{#if @pipeline.configPipelineId}}
<LinkTo
id="parent-pipeline-link"
class="header-item"
@route="v2.pipeline"
@model={{@pipeline.configPipelineId}}
>
<FaIcon @icon="cog" />
Parent Pipeline
</LinkTo>
{{/if}}

{{#if @pipeline.badges.sonar}}
<a
id="sonarqube-link"
class="header-item"
href={{this.sonarBadgeUri}}
title={{this.sonarBadgeDescription}}
target="_blank"
rel="noopener"
>
{{svg-jar "sonarqube" class="img sonarqube"}}
</a>
{{/if}}

<a
id="scm-link"
href="{{branch-url-encode @pipeline.scmRepo.url}}"
class="header-item scm"
>
<FaIcon @icon={{this.scmContext.scmIcon}} @prefix="fab" />
{{this.scmContext.scm}}
</a>

<BsDropdown id='repo-pipelines' class="header-item" as |dd|>
<dd.toggle>
<FaIcon @icon="code-branch" @title="Source code" />
<span class="branch">
{{@pipeline.scmRepo.branch}}
</span>
<BsTooltip @placement="bottom" @renderInPlace={{false}} @triggerEvents="hover" class="tooltip">
Switch to another Pipeline with the same repository
</BsTooltip>
</dd.toggle>
<dd.menu as |ddm|>
{{#each (await this.sameRepoPipeline) as |pipe|}}
<ddm.item>
<LinkTo
@route="v2.pipeline"
@model={{pipe.id}}
>
{{svg-jar "link" class="img"}} {{pipe.branchAndRootDir}}
</LinkTo>
</ddm.item>
{{else}}
<span>
No other Pipelines with the same repository
</span>
{{/each}}
</dd.menu>
</BsDropdown>

<BsButton
id="add-to-collection"
class="confirm"
@defaultText="Add to collection"
@type="primary"
@outline={{true}}
@onClick={{this.openAddToCollectionModal}}
/>

{{#if this.addToCollectionModalOpen}}
<Collection::Modal::AddToCollection
@pipeline={{@pipeline}}
@collections={{this.collections}}
@errorMessage={{this.errorMessage}}
@closeModal={{this.closeAddToCollectionModal}}
/>
{{/if}}
</div>
2 changes: 2 additions & 0 deletions app/components/pipeline/styles.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
@use 'nav/styles' as nav;
@use 'header/styles' as header;
@use 'modal/confirm-action/styles' as confirm-action-modal;
@use 'modal/toggle-job/styles' as toggle-job;
@use 'parameters/styles' as parameters;

@mixin styles {
@include nav.styles;
@include header.styles;
@include confirm-action-modal.styles;
@include toggle-job.styles;
@include parameters.styles;
Expand Down
12 changes: 0 additions & 12 deletions app/v2/pipeline/controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Controller from '@ember/controller';
import { service } from '@ember/service';
import { action } from '@ember/object';

export default class NewPipelineController extends Controller {
@service session;
Expand All @@ -24,15 +23,4 @@ export default class NewPipelineController extends Controller {
'v2.pipeline.jobs.index'
].includes(currentRouteName);
}

@action
addToCollection(pipelineId, collection) {
const { pipelineIds } = collection;

if (!pipelineIds.includes(pipelineId)) {
collection.set('pipelineIds', [...pipelineIds, pipelineId]);
}

return collection.save();
}
}
2 changes: 1 addition & 1 deletion app/v2/pipeline/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
'pipeline-nav pipeline-header'
'pipeline-nav pipeline-main';

.pipeline-header {
#pipeline-header {
grid-area: pipeline-header;
}

Expand Down
7 changes: 4 additions & 3 deletions app/v2/pipeline/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
<div class="pipeline-page">
<Pipeline::Nav />

<div class="pipeline-header">
<!-- TODO: need a new pipeline header component that does not rely on ember data -->
</div>
<Pipeline::Header
@pipeline={{this.pipeline}}
@collections={{this.collections}}
/>

<div class="pipeline-main-content {{if this.isEventsPullsJobsRoute "grid"}}">
{{outlet}}
Expand Down
Loading

0 comments on commit cde3c83

Please sign in to comment.