Improvements for action detail page (#24718)

Close #24625 

Main changes:

1. For the left panel, show rerun icon only on hover, and add style when
the job is selected, and removed icon on the "rerun all" button and
modify the text on the button


https://github.com/go-gitea/gitea/assets/17645053/cc437a17-d2e9-4f1b-a8cf-f56e53962767

2. Adjust fonts, and add on hover effects to the log lines. And add
loading effect when the job is done and the job step log is expanded for
the first time. (With reference to github)


https://github.com/go-gitea/gitea/assets/17645053/2808d77d-f402-4fb0-8819-7aa0a018cf0c

3. Add `gt-ellipsis` to `step-summary-msg` and `job-brief-name`

<img width="898" alt="ellipsis"
src="https://github.com/go-gitea/gitea/assets/17645053/e2fb7049-3125-4252-970d-15b0751febc7">

4. Fixed
https://github.com/go-gitea/gitea/issues/24625#issuecomment-1541380010
by adding explicit conditions to `ActionRunStatus.vue` and `status.tmpl`

5. Adjust some css styles

---------

Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
HesterG 2023-05-22 12:17:24 +08:00 committed by GitHub
parent cdb088cec2
commit da461b5a08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 130 additions and 54 deletions

View file

@ -80,6 +80,7 @@ milestones = Milestones
ok = OK ok = OK
cancel = Cancel cancel = Cancel
rerun = Re-run rerun = Re-run
rerun_all = Re-run all jobs
save = Save save = Save
add = Add add = Add
add_all = Add All add_all = Add All

View file

@ -1,5 +1,6 @@
<!-- This template should be kept the same as web_src/js/components/ActionRunStatus.vue <!-- This template should be kept the same as web_src/js/components/ActionRunStatus.vue
Please also update the vue file above if this template is modified. Please also update the vue file above if this template is modified.
action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown
--> -->
{{- $size := 16 -}} {{- $size := 16 -}}
{{- if .size -}} {{- if .size -}}
@ -11,7 +12,7 @@
{{- $className = .className -}} {{- $className = .className -}}
{{- end -}} {{- end -}}
<span data-tooltip-content="{{.locale.Tr (printf "actions.status.%s" .status)}}"> <span class="gt-df gt-ac" data-tooltip-content="{{.locale.Tr (printf "actions.status.%s" .status)}}">
{{if eq .status "success"}} {{if eq .status "success"}}
{{svg "octicon-check-circle-fill" $size (printf "text green %s" $className)}} {{svg "octicon-check-circle-fill" $size (printf "text green %s" $className)}}
{{else if eq .status "skipped"}} {{else if eq .status "skipped"}}
@ -22,7 +23,7 @@
{{svg "octicon-blocked" $size (printf "text yellow %s" $className)}} {{svg "octicon-blocked" $size (printf "text yellow %s" $className)}}
{{else if eq .status "running"}} {{else if eq .status "running"}}
{{svg "octicon-meter" $size (printf "text yellow job-status-rotate %s" $className)}} {{svg "octicon-meter" $size (printf "text yellow job-status-rotate %s" $className)}}
{{else}} {{else if or (eq .status "failure") or (eq .status "cancelled") or (eq .status "unknown")}}
{{svg "octicon-x-circle-fill" $size (printf "text red %s" $className)}} {{svg "octicon-x-circle-fill" $size (printf "text red %s" $className)}}
{{end}} {{end}}
</span> </span>

View file

@ -9,6 +9,7 @@
data-locale-approve="{{.locale.Tr "repo.diff.review.approve"}}" data-locale-approve="{{.locale.Tr "repo.diff.review.approve"}}"
data-locale-cancel="{{.locale.Tr "cancel"}}" data-locale-cancel="{{.locale.Tr "cancel"}}"
data-locale-rerun="{{.locale.Tr "rerun"}}" data-locale-rerun="{{.locale.Tr "rerun"}}"
data-locale-rerun-all="{{.locale.Tr "rerun_all"}}"
data-locale-status-unknown="{{.locale.Tr "actions.status.unknown"}}" data-locale-status-unknown="{{.locale.Tr "actions.status.unknown"}}"
data-locale-status-waiting="{{.locale.Tr "actions.status.waiting"}}" data-locale-status-waiting="{{.locale.Tr "actions.status.waiting"}}"
data-locale-status-running="{{.locale.Tr "actions.status.running"}}" data-locale-status-running="{{.locale.Tr "actions.status.running"}}"

View file

@ -71,6 +71,7 @@
/* console colors */ /* console colors */
--color-console-fg: #ffffff; --color-console-fg: #ffffff;
--color-console-bg: #171717; --color-console-bg: #171717;
--color-console-hover-bg: #ffffff16;
/* named colors */ /* named colors */
--color-red: #db2828; --color-red: #db2828;
--color-orange: #f2711c; --color-orange: #f2711c;

View file

@ -1,14 +1,15 @@
<!-- This vue should be kept the same as templates/repo/actions/status.tmpl <!-- This vue should be kept the same as templates/repo/actions/status.tmpl
Please also update the template file above if this vue is modified. Please also update the template file above if this vue is modified.
action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown
--> -->
<template> <template>
<span :data-tooltip-content="localeStatus" v-if="status"> <span class="gt-df gt-ac" :data-tooltip-content="localeStatus" v-if="status">
<SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/> <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/>
<SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/> <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/>
<SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/> <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/>
<SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'blocked'"/> <SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'blocked'"/>
<SvgIcon name="octicon-meter" class="text yellow" :size="size" :class-name="'job-status-rotate ' + className" v-else-if="status === 'running'"/> <SvgIcon name="octicon-meter" class="text yellow" :size="size" :class-name="'job-status-rotate ' + className" v-else-if="status === 'running'"/>
<SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else/> <SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else-if="['failure', 'cancelled', 'unknown'].includes(status)" />
</span> </span>
</template> </template>

View file

@ -1,28 +1,30 @@
<template> <template>
<div class="action-view-container"> <div class="ui container action-view-container">
<div class="action-view-header"> <div class="action-view-header">
<div class="action-info-summary"> <div class="action-info-summary">
<div class="action-info-summary-title">
<ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/> <ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/>
<div class="action-title"> <h2 class="action-info-summary-title-text">
{{ run.title }} {{ run.title }}
</h2>
</div> </div>
<button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove"> <button class="ui basic small compact button primary" @click="approveRun()" v-if="run.canApprove">
<SvgIcon class="gt-mr-2" name="octicon-play" :size="20"/> {{ locale.approve }} {{ locale.approve }}
</button> </button>
<button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel"> <button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel">
<SvgIcon class="gt-mr-2" name="octicon-x-circle-fill" :size="20"/> {{ locale.cancel }} {{ locale.cancel }}
</button> </button>
<button class="ui basic small compact button secondary" @click="rerun()" v-else-if="run.canRerun"> <button class="ui basic small compact button secondary" @click="rerun()" v-else-if="run.canRerun">
<SvgIcon class="gt-mr-2" name="octicon-sync" :size="20"/> {{ locale.rerun }} {{ locale.rerun_all }}
</button> </button>
</div> </div>
<div class="action-commit-summary"> <div class="action-commit-summary">
{{ run.commit.localeCommit }} {{ run.commit.localeCommit }}
<a :href="run.commit.link">{{ run.commit.shortSHA }}</a> <a :href="run.commit.link">{{ run.commit.shortSHA }}</a>
&nbsp;<span class="ui label" v-if="run.commit.shortSHA"> <span class="ui label" v-if="run.commit.shortSHA">
<a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a> <a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
</span> </span>
&nbsp;{{ run.commit.localePushedBy }} {{ run.commit.localePushedBy }}
<a :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a> <a :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
</div> </div>
</div> </div>
@ -30,15 +32,15 @@
<div class="action-view-left"> <div class="action-view-left">
<div class="job-group-section"> <div class="job-group-section">
<div class="job-brief-list"> <div class="job-brief-list">
<div class="job-brief-item" v-for="(job, index) in run.jobs" :key="job.id"> <div class="job-brief-item" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1">
<a class="job-brief-link" :href="run.link+'/jobs/'+index"> <a class="job-brief-link" :href="run.link+'/jobs/'+index">
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/> <ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
<span class="ui text gt-mx-3">{{ job.name }}</span> <span class="job-brief-name gt-mx-3 gt-ellipsis">{{ job.name }}</span>
</a> </a>
<span class="job-brief-info">
<span class="step-summary-duration">{{ job.duration }}</span> <span class="step-summary-duration">{{ job.duration }}</span>
<button :data-tooltip-content="locale.rerun" class="job-brief-rerun" @click="rerunJob(index)" v-if="job.canRerun"> <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun gt-mx-3" @click="rerunJob(index)" v-if="job.canRerun && onHoverRerunIndex === job.id"/>
<SvgIcon name="octicon-sync" class="ui text black"/> </span>
</button>
</div> </div>
</div> </div>
</div> </div>
@ -58,21 +60,24 @@
<div class="action-view-right"> <div class="action-view-right">
<div class="job-info-header"> <div class="job-info-header">
<div class="job-info-header-title"> <h3 class="job-info-header-title">
{{ currentJob.title }} {{ currentJob.title }}
</div> </h3>
<div class="job-info-header-detail"> <p class="job-info-header-detail">
{{ currentJob.detail }} {{ currentJob.detail }}
</div> </p>
</div> </div>
<div class="job-step-container"> <div class="job-step-container">
<div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
<div class="job-step-summary" @click.stop="toggleStepLogs(i)"> <div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''">
<SvgIcon :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/> <!-- If the job is done and the job step log is loaded for the first time, show the loading icon
currentJobStepsStates[i].cursor === null means the log is loaded for the first time
-->
<SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
<SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/>
<ActionRunStatus :status="jobStep.status" class="gt-mr-3"/> <ActionRunStatus :status="jobStep.status" class="gt-mr-3"/>
<span class="step-summary-msg">{{ jobStep.summary }}</span> <span class="step-summary-msg gt-ellipsis">{{ jobStep.summary }}</span>
<span class="step-summary-duration">{{ jobStep.duration }}</span> <span class="step-summary-duration">{{ jobStep.duration }}</span>
</div> </div>
@ -115,6 +120,7 @@ const sfc = {
intervalID: null, intervalID: null,
currentJobStepsStates: [], currentJobStepsStates: [],
artifacts: [], artifacts: [],
onHoverRerunIndex: -1,
// provided by backend // provided by backend
run: { run: {
@ -295,6 +301,7 @@ const sfc = {
// sync the currentJobStepsStates to store the job step states // sync the currentJobStepsStates to store the job step states
for (let i = 0; i < this.currentJob.steps.length; i++) { for (let i = 0; i < this.currentJob.steps.length; i++) {
if (!this.currentJobStepsStates[i]) { if (!this.currentJobStepsStates[i]) {
// initial states for job steps
this.currentJobStepsStates[i] = {cursor: null, expanded: false}; this.currentJobStepsStates[i] = {cursor: null, expanded: false};
} }
} }
@ -325,6 +332,10 @@ const sfc = {
body, body,
}); });
}, },
isDone(status) {
return ['success', 'skipped', 'failure', 'cancelled'].includes(status);
}
}, },
}; };
@ -348,6 +359,7 @@ export function initRepositoryActionView() {
cancel: el.getAttribute('data-locale-cancel'), cancel: el.getAttribute('data-locale-cancel'),
rerun: el.getAttribute('data-locale-rerun'), rerun: el.getAttribute('data-locale-rerun'),
artifactsTitle: el.getAttribute('data-locale-artifacts-title'), artifactsTitle: el.getAttribute('data-locale-artifacts-title'),
rerun_all: el.getAttribute('data-locale-rerun-all'),
status: { status: {
unknown: el.getAttribute('data-locale-status-unknown'), unknown: el.getAttribute('data-locale-status-unknown'),
waiting: el.getAttribute('data-locale-status-waiting'), waiting: el.getAttribute('data-locale-status-waiting'),
@ -417,24 +429,30 @@ export function ansiLogToHTML(line) {
/* action view header */ /* action view header */
.action-view-header { .action-view-header {
margin: 0 20px 20px 20px; margin: 20px 0px;
} }
.action-info-summary { .action-info-summary {
font-size: 150%;
height: 20px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 1rem; margin-top: 1rem;
justify-content: space-between;
} }
.action-info-summary .action-title { .action-info-summary-title {
padding: 0 5px; display: flex;
}
.action-info-summary-title-text {
font-size: 20px;
margin: 0 0 0 5px;
flex: 1; flex: 1;
} }
.action-commit-summary { .action-commit-summary {
padding: 10px 10px; display: flex;
gap: 5px;
margin: 10px 0px 10px 25px;
} }
/* ================ */ /* ================ */
@ -444,7 +462,6 @@ export function ansiLogToHTML(line) {
width: 30%; width: 30%;
max-width: 400px; max-width: 400px;
overflow-y: scroll; overflow-y: scroll;
margin-left: 10px;
} }
.job-group-section .job-group-summary { .job-group-section .job-group-summary {
@ -473,42 +490,64 @@ export function ansiLogToHTML(line) {
padding-right: 3px; padding-right: 3px;
} }
.job-group-section .job-brief-list .job-brief-item { .job-brief-item {
margin: 5px 0; margin: 5px 0;
padding: 10px; padding: 10px;
background: var(--color-info-bg); background: var(--color-info-bg);
border-radius: 5px; border-radius: 5px;
text-decoration: none; text-decoration: none;
display: flex; display: flex;
justify-items: center;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
} }
.job-group-section .job-brief-list .job-brief-item .job-brief-rerun { .job-brief-item:hover {
float: right; background-color: var(--color-secondary);
border: none; }
background-color: transparent;
outline: none; .job-brief-item.selected {
font-weight: var(--font-weight-bold);
background-color: var(--color-secondary-dark-1);
}
.job-brief-item:first-of-type {
margin-top: 0;
}
.job-brief-item .job-brief-rerun {
cursor: pointer; cursor: pointer;
transition: transform 0.2s; transition: transform 0.2s;
} }
.job-group-section .job-brief-list .job-brief-item .job-brief-rerun:hover { .job-brief-item .job-brief-rerun:hover {
transform: scale(130%); transform: scale(130%);
} }
.job-group-section .job-brief-list .job-brief-item .job-brief-link { .job-brief-item .job-brief-link {
flex-grow: 1;
display: flex; display: flex;
width: 100%;
} }
.job-group-section .job-brief-list .job-brief-item .job-brief-link span { .job-brief-item .job-brief-link span {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.job-group-section .job-brief-list .job-brief-item:hover { .job-brief-item .job-brief-link .job-brief-name {
background-color: var(--color-secondary); display: block;
width: 70%;
color: var(--color-text);
}
.job-brief-item .job-brief-link:hover {
text-decoration: none;
}
.job-brief-item .job-brief-info {
display: flex;
align-items: center;
width: 55px;
} }
/* ================ */ /* ================ */
@ -517,21 +556,27 @@ export function ansiLogToHTML(line) {
.action-view-right { .action-view-right {
flex: 1; flex: 1;
background-color: var(--color-console-bg); background-color: var(--color-console-bg);
color: var(--color-console-fg); color: var(--color-secondary-dark-2);
max-height: 100%; max-height: 100%;
margin-right: 10px; width: 70%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.job-info-header .job-info-header-title { .job-info-header {
font-size: 150%;
padding: 10px; padding: 10px;
border-bottom: 1px solid var(--color-grey);
}
.job-info-header .job-info-header-title {
color: var(--color-console-fg);
font-size: 16px;
margin: 0;
} }
.job-info-header .job-info-header-detail { .job-info-header .job-info-header-detail {
padding: 0 10px 10px; color: var(--color-secondary-dark-3);
border-bottom: 1px solid var(--color-grey); font-size: 12px;
} }
.job-step-container { .job-step-container {
@ -543,6 +588,8 @@ export function ansiLogToHTML(line) {
cursor: pointer; cursor: pointer;
padding: 5px 10px; padding: 5px 10px;
display: flex; display: flex;
align-items: center;
user-select: none;
} }
.job-step-container .job-step-summary .step-summary-msg { .job-step-container .job-step-summary .step-summary-msg {
@ -553,8 +600,25 @@ export function ansiLogToHTML(line) {
margin-left: 16px; margin-left: 16px;
} }
.job-step-container .job-step-summary:hover { .job-step-container .job-step-summary:hover,
.job-step-container .job-step-summary.selected {
color: var(--color-console-fg);
background-color: var(--color-black-light); background-color: var(--color-black-light);
border-radius: 5px;
}
@media (max-width: 768px) {
.action-view-body {
flex-direction: column;
}
.action-view-left, .action-view-right {
width: 100%;
}
.action-view-left {
max-width: none;
overflow-y: hidden;
}
} }
</style> </style>
@ -576,12 +640,19 @@ export function ansiLogToHTML(line) {
.job-step-section .job-step-logs { .job-step-section .job-step-logs {
font-family: monospace, monospace; font-family: monospace, monospace;
margin: 8px 0px;
font-size: 12px;
} }
.job-step-section .job-step-logs .job-log-line { .job-step-section .job-step-logs .job-log-line {
display: flex; display: flex;
} }
.job-step-section .job-step-logs .job-log-line:hover {
color: var(--color-console-fg);
background-color: var(--color-console-hover-bg);
}
.job-step-section .job-step-logs .job-log-line .line-num { .job-step-section .job-step-logs .job-log-line .line-num {
width: 48px; width: 48px;
color: var(--color-grey-light); color: var(--color-grey-light);