mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-23 14:53:34 +01:00
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:
parent
cdb088cec2
commit
da461b5a08
6 changed files with 130 additions and 54 deletions
|
@ -80,6 +80,7 @@ milestones = Milestones
|
|||
ok = OK
|
||||
cancel = Cancel
|
||||
rerun = Re-run
|
||||
rerun_all = Re-run all jobs
|
||||
save = Save
|
||||
add = Add
|
||||
add_all = Add All
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!-- 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.
|
||||
action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown
|
||||
-->
|
||||
{{- $size := 16 -}}
|
||||
{{- if .size -}}
|
||||
|
@ -11,7 +12,7 @@
|
|||
{{- $className = .className -}}
|
||||
{{- 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"}}
|
||||
{{svg "octicon-check-circle-fill" $size (printf "text green %s" $className)}}
|
||||
{{else if eq .status "skipped"}}
|
||||
|
@ -22,7 +23,7 @@
|
|||
{{svg "octicon-blocked" $size (printf "text yellow %s" $className)}}
|
||||
{{else if eq .status "running"}}
|
||||
{{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)}}
|
||||
{{end}}
|
||||
</span>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
data-locale-approve="{{.locale.Tr "repo.diff.review.approve"}}"
|
||||
data-locale-cancel="{{.locale.Tr "cancel"}}"
|
||||
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-waiting="{{.locale.Tr "actions.status.waiting"}}"
|
||||
data-locale-status-running="{{.locale.Tr "actions.status.running"}}"
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
/* console colors */
|
||||
--color-console-fg: #ffffff;
|
||||
--color-console-bg: #171717;
|
||||
--color-console-hover-bg: #ffffff16;
|
||||
/* named colors */
|
||||
--color-red: #db2828;
|
||||
--color-orange: #f2711c;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
<!-- 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.
|
||||
action status accepted: success, skipped, waiting, blocked, running, failure, cancelled, unknown
|
||||
-->
|
||||
<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-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-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-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>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,28 +1,30 @@
|
|||
<template>
|
||||
<div class="action-view-container">
|
||||
<div class="ui container action-view-container">
|
||||
<div class="action-view-header">
|
||||
<div class="action-info-summary">
|
||||
<ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/>
|
||||
<div class="action-title">
|
||||
{{ run.title }}
|
||||
<div class="action-info-summary-title">
|
||||
<ActionRunStatus :locale-status="locale.status[run.status]" :status="run.status" :size="20"/>
|
||||
<h2 class="action-info-summary-title-text">
|
||||
{{ run.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<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 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 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>
|
||||
</div>
|
||||
<div class="action-commit-summary">
|
||||
{{ run.commit.localeCommit }}
|
||||
<a :href="run.commit.link">{{ run.commit.shortSHA }}</a>
|
||||
<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>
|
||||
</span>
|
||||
{{ run.commit.localePushedBy }}
|
||||
{{ run.commit.localePushedBy }}
|
||||
<a :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,15 +32,15 @@
|
|||
<div class="action-view-left">
|
||||
<div class="job-group-section">
|
||||
<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">
|
||||
<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>
|
||||
<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" class="ui text black"/>
|
||||
</button>
|
||||
<span class="job-brief-info">
|
||||
<span class="step-summary-duration">{{ job.duration }}</span>
|
||||
<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"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -58,21 +60,24 @@
|
|||
|
||||
<div class="action-view-right">
|
||||
<div class="job-info-header">
|
||||
<div class="job-info-header-title">
|
||||
<h3 class="job-info-header-title">
|
||||
{{ currentJob.title }}
|
||||
</div>
|
||||
<div class="job-info-header-detail">
|
||||
</h3>
|
||||
<p class="job-info-header-detail">
|
||||
{{ currentJob.detail }}
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
<div class="job-step-container">
|
||||
<div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
|
||||
<div class="job-step-summary" @click.stop="toggleStepLogs(i)">
|
||||
<SvgIcon :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/>
|
||||
|
||||
<div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''">
|
||||
<!-- 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"/>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
@ -115,6 +120,7 @@ const sfc = {
|
|||
intervalID: null,
|
||||
currentJobStepsStates: [],
|
||||
artifacts: [],
|
||||
onHoverRerunIndex: -1,
|
||||
|
||||
// provided by backend
|
||||
run: {
|
||||
|
@ -295,6 +301,7 @@ const sfc = {
|
|||
// sync the currentJobStepsStates to store the job step states
|
||||
for (let i = 0; i < this.currentJob.steps.length; i++) {
|
||||
if (!this.currentJobStepsStates[i]) {
|
||||
// initial states for job steps
|
||||
this.currentJobStepsStates[i] = {cursor: null, expanded: false};
|
||||
}
|
||||
}
|
||||
|
@ -325,6 +332,10 @@ const sfc = {
|
|||
body,
|
||||
});
|
||||
},
|
||||
|
||||
isDone(status) {
|
||||
return ['success', 'skipped', 'failure', 'cancelled'].includes(status);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -348,6 +359,7 @@ export function initRepositoryActionView() {
|
|||
cancel: el.getAttribute('data-locale-cancel'),
|
||||
rerun: el.getAttribute('data-locale-rerun'),
|
||||
artifactsTitle: el.getAttribute('data-locale-artifacts-title'),
|
||||
rerun_all: el.getAttribute('data-locale-rerun-all'),
|
||||
status: {
|
||||
unknown: el.getAttribute('data-locale-status-unknown'),
|
||||
waiting: el.getAttribute('data-locale-status-waiting'),
|
||||
|
@ -417,24 +429,30 @@ export function ansiLogToHTML(line) {
|
|||
/* action view header */
|
||||
|
||||
.action-view-header {
|
||||
margin: 0 20px 20px 20px;
|
||||
margin: 20px 0px;
|
||||
}
|
||||
|
||||
.action-info-summary {
|
||||
font-size: 150%;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.action-info-summary .action-title {
|
||||
padding: 0 5px;
|
||||
.action-info-summary-title {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.action-info-summary-title-text {
|
||||
font-size: 20px;
|
||||
margin: 0 0 0 5px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.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%;
|
||||
max-width: 400px;
|
||||
overflow-y: scroll;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.job-group-section .job-group-summary {
|
||||
|
@ -473,42 +490,64 @@ export function ansiLogToHTML(line) {
|
|||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.job-group-section .job-brief-list .job-brief-item {
|
||||
.job-brief-item {
|
||||
margin: 5px 0;
|
||||
padding: 10px;
|
||||
background: var(--color-info-bg);
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.job-group-section .job-brief-list .job-brief-item .job-brief-rerun {
|
||||
float: right;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
.job-brief-item:hover {
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.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;
|
||||
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%);
|
||||
}
|
||||
|
||||
.job-group-section .job-brief-list .job-brief-item .job-brief-link {
|
||||
flex-grow: 1;
|
||||
.job-brief-item .job-brief-link {
|
||||
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;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.job-group-section .job-brief-list .job-brief-item:hover {
|
||||
background-color: var(--color-secondary);
|
||||
.job-brief-item .job-brief-link .job-brief-name {
|
||||
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 {
|
||||
flex: 1;
|
||||
background-color: var(--color-console-bg);
|
||||
color: var(--color-console-fg);
|
||||
color: var(--color-secondary-dark-2);
|
||||
max-height: 100%;
|
||||
margin-right: 10px;
|
||||
width: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.job-info-header .job-info-header-title {
|
||||
font-size: 150%;
|
||||
.job-info-header {
|
||||
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 {
|
||||
padding: 0 10px 10px;
|
||||
border-bottom: 1px solid var(--color-grey);
|
||||
color: var(--color-secondary-dark-3);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.job-step-container {
|
||||
|
@ -543,6 +588,8 @@ export function ansiLogToHTML(line) {
|
|||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.job-step-container .job-step-summary .step-summary-msg {
|
||||
|
@ -553,8 +600,25 @@ export function ansiLogToHTML(line) {
|
|||
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);
|
||||
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>
|
||||
|
||||
|
@ -576,12 +640,19 @@ export function ansiLogToHTML(line) {
|
|||
|
||||
.job-step-section .job-step-logs {
|
||||
font-family: monospace, monospace;
|
||||
margin: 8px 0px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.job-step-section .job-step-logs .job-log-line {
|
||||
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 {
|
||||
width: 48px;
|
||||
color: var(--color-grey-light);
|
||||
|
|
Loading…
Reference in a new issue