mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-10 02:53:48 +00:00
enhance(actions): Make Summary UI more beautiful with more infos (#37824)
## Summary - Redesign the Actions run summary header to follow GitHub Actions layout: trigger info on the left, Status / Total duration / Artifacts columns inline on the right - Expose trigger user avatar, pull request link, and PR head branch info from the run view API - Update the workflow graph header to show the workflow filename (linked to the run workflow file) and `on: <event>`, while keeping the jobs/dependencies/success stats line - Remove the redundant commit/workflow metadata row below the run title; that information now lives in the summary bar New: <img width="1564" height="639" src="https://github.com/user-attachments/assets/e6bc1623-c5fc-4e97-abc9-fde7f3c6aef9" /> Old: <img width="2038" height="1038" src="https://github.com/user-attachments/assets/0857f19a-8d3a-4da2-82fd-e9ebeb200062" /> Replaces https://github.com/go-gitea/gitea/pull/36721 --------- Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import ActionStatusIcon from './ActionStatusIcon.vue';
|
||||
import WorkflowGraph from './WorkflowGraph.vue';
|
||||
import type {ActionRunViewStore} from "./ActionRunView.ts";
|
||||
import {computed, onBeforeUnmount, onMounted, toRefs} from "vue";
|
||||
@@ -11,6 +10,7 @@ defineOptions({
|
||||
const props = defineProps<{
|
||||
store: ActionRunViewStore;
|
||||
locale: Record<string, any>;
|
||||
artifactCount: number;
|
||||
}>();
|
||||
|
||||
const locale = props.locale;
|
||||
@@ -25,12 +25,27 @@ const topLevelJobs = computed(() => (run.value.jobs || []).filter((j) => !j.pare
|
||||
const triggerUser = computed(() => {
|
||||
const currentAttempt = run.value.attempts.find((attempt) => attempt.current);
|
||||
if (currentAttempt) {
|
||||
return {name: currentAttempt.triggerUserName, link: currentAttempt.triggerUserLink};
|
||||
return {
|
||||
name: currentAttempt.triggerUserName,
|
||||
link: currentAttempt.triggerUserLink,
|
||||
avatar: currentAttempt.triggerUserAvatar,
|
||||
};
|
||||
}
|
||||
const pusher = run.value.commit.pusher;
|
||||
return pusher.displayName ? {name: pusher.displayName, link: pusher.link} : null;
|
||||
return pusher.displayName ? {
|
||||
name: pusher.displayName,
|
||||
link: pusher.link,
|
||||
avatar: pusher.avatarLink,
|
||||
} : null;
|
||||
});
|
||||
|
||||
const triggerLabel = computed(() => {
|
||||
if (isRerun.value) return locale.rerunTriggered;
|
||||
return locale.triggeredVia.replace('%s', run.value.triggerEvent);
|
||||
});
|
||||
|
||||
const artifactsDisplay = computed(() => props.artifactCount > 0 ? String(props.artifactCount) : '–');
|
||||
|
||||
onMounted(async () => {
|
||||
await props.store.startPollingCurrentRun();
|
||||
});
|
||||
@@ -42,19 +57,60 @@ onBeforeUnmount(() => {
|
||||
<template>
|
||||
<div class="action-run-summary-view">
|
||||
<div class="action-run-summary-block">
|
||||
<div class="flex-text-block">
|
||||
<span>{{ isRerun ? locale.rerun : locale.triggeredVia.replace('%s', run.triggerEvent) }}</span>
|
||||
<template v-if="triggerUser">
|
||||
<span>•</span>
|
||||
<a v-if="triggerUser.link" class="muted" :href="triggerUser.link">{{ triggerUser.name }}</a>
|
||||
<span v-else class="muted">{{ triggerUser.name }}</span>
|
||||
</template>
|
||||
<span>•</span>
|
||||
<relative-time :datetime="run.triggeredAt || ''" prefix=""/>
|
||||
<div class="action-run-summary-trigger">
|
||||
<span class="action-run-summary-label">
|
||||
{{ triggerLabel }} <relative-time :datetime="run.triggeredAt || ''" prefix=""/>
|
||||
</span>
|
||||
<div class="flex-text-block tw-flex-wrap action-run-summary-trigger-content">
|
||||
<component
|
||||
:is="triggerUser.link ? 'a' : 'span'"
|
||||
v-if="triggerUser"
|
||||
class="flex-text-inline action-run-summary-user"
|
||||
:class="{silenced: triggerUser.link}"
|
||||
:href="triggerUser.link || undefined"
|
||||
>
|
||||
<img
|
||||
v-if="triggerUser.avatar"
|
||||
class="ui avatar tw-align-middle"
|
||||
:src="triggerUser.avatar"
|
||||
width="16"
|
||||
height="16"
|
||||
:alt="triggerUser.name"
|
||||
>
|
||||
<span>{{ triggerUser.name }}</span>
|
||||
</component>
|
||||
<a v-if="run.pullRequest" class="action-run-summary-pr silenced" :href="run.pullRequest.link">{{ run.pullRequest.index }}</a>
|
||||
<span v-else-if="run.commit.branch.name" class="action-run-summary-branch-label tw-max-w-full">
|
||||
<a
|
||||
v-if="!run.commit.branch.isDeleted && run.commit.branch.link"
|
||||
class="gt-ellipsis silenced"
|
||||
:href="run.commit.branch.link"
|
||||
:title="run.commit.branch.name"
|
||||
>{{ run.commit.branch.name }}</a>
|
||||
<span
|
||||
v-else
|
||||
class="gt-ellipsis tw-line-through"
|
||||
:title="run.commit.branch.name"
|
||||
>{{ run.commit.branch.name }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-text-block">
|
||||
<ActionStatusIcon :locale-status="locale.status[run.status]" :status="run.status" :size="16" icon-variant="circle-fill"/>
|
||||
<span>{{ locale.status[run.status] }}</span> • <span>{{ locale.totalDuration }} {{ run.duration || '–' }}</span>
|
||||
|
||||
<div class="action-run-summary-stat-divider"/>
|
||||
|
||||
<div class="action-run-summary-stat">
|
||||
<span class="action-run-summary-label">{{ locale.statusLabel }}</span>
|
||||
<span class="action-run-summary-stat-value">{{ locale.status[run.status] }}</span>
|
||||
</div>
|
||||
|
||||
<div class="action-run-summary-stat">
|
||||
<span class="action-run-summary-label">{{ locale.totalDuration }}</span>
|
||||
<span class="action-run-summary-stat-value">{{ run.duration || '–' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="action-run-summary-stat action-run-summary-stat-last">
|
||||
<span class="action-run-summary-label">{{ locale.artifactsTitle }}</span>
|
||||
<span class="action-run-summary-stat-value">{{ artifactsDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<WorkflowGraph
|
||||
@@ -63,6 +119,8 @@ onBeforeUnmount(() => {
|
||||
:jobs="topLevelJobs"
|
||||
:run-link="run.link"
|
||||
:workflow-id="run.workflowID"
|
||||
:workflow-link="`${run.link}/workflow`"
|
||||
:trigger-event="run.triggerEvent"
|
||||
:locale="locale"
|
||||
/>
|
||||
</div>
|
||||
@@ -77,13 +135,119 @@ onBeforeUnmount(() => {
|
||||
|
||||
.action-run-summary-block {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
align-items: stretch; /* equal-height columns so labels align at top and values at bottom */
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
background: var(--color-box-header);
|
||||
background: var(--color-console-bg);
|
||||
}
|
||||
|
||||
.action-run-summary-trigger {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.action-run-summary-label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--color-text-light-2);
|
||||
}
|
||||
|
||||
.action-run-summary-trigger-content {
|
||||
margin-top: auto; /* pin trigger content to the bottom, aligned with the stat values */
|
||||
color: var(--color-text-light-2);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-run-summary-user {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text);
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.action-run-summary-user .ui.avatar {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.action-run-summary-pr {
|
||||
color: var(--color-text);
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.action-run-summary-branch-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: 200px;
|
||||
min-height: 20px;
|
||||
padding: 0 6px;
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--color-primary-light-6);
|
||||
color: var(--color-primary);
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
font-family: var(--fonts-monospace);
|
||||
}
|
||||
|
||||
.action-run-summary-branch-label a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.action-run-summary-branch-label a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.action-run-summary-user:hover span {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.action-run-summary-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 auto;
|
||||
min-width: 72px;
|
||||
margin-left: 24px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.action-run-summary-stat-last {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.action-run-summary-stat-divider {
|
||||
display: none;
|
||||
flex: 0 0 100%;
|
||||
margin: 8px 0;
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.action-run-summary-stat-value {
|
||||
display: block;
|
||||
margin-top: auto; /* pin value to the bottom so all column values share a baseline */
|
||||
font-size: 16px;
|
||||
line-height: 1.25;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.action-run-summary-trigger {
|
||||
flex: 0 0 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.action-run-summary-stat {
|
||||
margin-left: 0;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.action-run-summary-stat-divider {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user