From f6e7798405ef9eb7b2936b112fd2a4ea1bab4082 Mon Sep 17 00:00:00 2001
From: yp05327 <576951401@qq.com>
Date: Mon, 21 Aug 2023 16:26:10 +0900
Subject: [PATCH] Add link to job details and tooltip to commit status in repo
 list in dashboard (#26326)

Tooltip:

![image](https://github.com/go-gitea/gitea/assets/18380374/237cb545-7844-424b-b995-1008eaaaedec)

Link to the target job:

![image](https://github.com/go-gitea/gitea/assets/18380374/0c11a97f-6517-47f2-8773-f381488c084e)
---
 models/git/commit_status.go                 |  6 ++++++
 modules/structs/commit_status.go            |  4 ++++
 options/locale/locale_en-US.ini             |  5 +++++
 routers/web/repo/repo.go                    |  5 ++++-
 services/repository/repository.go           |  5 +++--
 web_src/js/components/DashboardRepoList.vue | 21 ++++++++++++++-------
 6 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/models/git/commit_status.go b/models/git/commit_status.go
index e7616fb167..24fee8c3b4 100644
--- a/models/git/commit_status.go
+++ b/models/git/commit_status.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/translation"
 
 	"xorm.io/builder"
 	"xorm.io/xorm"
@@ -191,6 +192,11 @@ func (status *CommitStatus) APIURL(ctx context.Context) string {
 	return status.Repo.APIURL() + "/statuses/" + url.PathEscape(status.SHA)
 }
 
+// LocaleString returns the locale string name of the Status
+func (status *CommitStatus) LocaleString(lang translation.Locale) string {
+	return lang.Tr("repo.commitstatus." + status.State.String())
+}
+
 // CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
 func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
 	var lastStatus *CommitStatus
diff --git a/modules/structs/commit_status.go b/modules/structs/commit_status.go
index de1d8fa566..fda795dca6 100644
--- a/modules/structs/commit_status.go
+++ b/modules/structs/commit_status.go
@@ -25,6 +25,10 @@ var commitStatusPriorities = map[CommitStatusState]int{
 	CommitStatusSuccess: 3,
 }
 
+func (css CommitStatusState) String() string {
+	return string(css)
+}
+
 // NoBetterThan returns true if this State is no better than the given State
 // This function only handles the states defined in CommitStatusPriorities
 func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 0bfe1eac75..2733f7df4d 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1280,6 +1280,11 @@ commit.cherry-pick = Cherry-pick
 commit.cherry-pick-header = Cherry-pick: %s
 commit.cherry-pick-content = Select branch to cherry-pick onto:
 
+commitstatus.error = Error
+commitstatus.failure = Failure
+commitstatus.pending = Pending
+commitstatus.success = Success
+
 ext_issues = Access to External Issues
 ext_issues.desc = Link to an external issue tracker.
 
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 0de804dbce..4409381bc5 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -600,6 +600,8 @@ func SearchRepo(ctx *context.Context) {
 
 	results := make([]*repo_service.WebSearchRepository, len(repos))
 	for i, repo := range repos {
+		latestCommitStatus := git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID])
+
 		results[i] = &repo_service.WebSearchRepository{
 			Repository: &api.Repository{
 				ID:       repo.ID,
@@ -613,7 +615,8 @@ func SearchRepo(ctx *context.Context) {
 				Link:     repo.Link(),
 				Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
 			},
-			LatestCommitStatus: git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID]),
+			LatestCommitStatus:       latestCommitStatus,
+			LocaleLatestCommitStatus: latestCommitStatus.LocaleString(ctx.Locale),
 		}
 	}
 
diff --git a/services/repository/repository.go b/services/repository/repository.go
index cd3658dcd8..ade747582f 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -28,8 +28,9 @@ import (
 
 // WebSearchRepository represents a repository returned by web search
 type WebSearchRepository struct {
-	Repository         *structs.Repository `json:"repository"`
-	LatestCommitStatus *git.CommitStatus   `json:"latest_commit_status"`
+	Repository               *structs.Repository `json:"repository"`
+	LatestCommitStatus       *git.CommitStatus   `json:"latest_commit_status"`
+	LocaleLatestCommitStatus string              `json:"locale_latest_commit_status"`
 }
 
 // WebSearchResults results of a successful web search
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index d68ee45ee3..008149ca60 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -69,16 +69,18 @@
       </div>
       <div v-if="repos.length" class="ui attached table segment gt-rounded-bottom">
         <ul class="repo-owner-name-list">
-          <li class="gt-df gt-ac" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
-            <a class="repo-list-link muted gt-df gt-ac gt-f1" :href="repo.link">
+          <li class="gt-df gt-ac gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
+            <a class="repo-list-link muted gt-df gt-ac gt-f1 gt-gap-3" :href="repo.link">
               <svg-icon :name="repoIcon(repo)" :size="16" class-name="repo-list-icon"/>
               <div class="text truncate">{{ repo.full_name }}</div>
               <div v-if="repo.archived">
                 <svg-icon name="octicon-archive" :size="16"/>
               </div>
             </a>
-            <!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
-            <svg-icon v-if="repo.latest_commit_status_state" :name="statusIcon(repo.latest_commit_status_state)" :class-name="'gt-ml-3 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/>
+            <a class="gt-df gt-ac" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link" :data-tooltip-content="repo.locale_latest_commit_status_state">
+              <!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
+              <svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class-name="'gt-ml-3 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/>
+            </a>
           </li>
         </ul>
         <div v-if="showMoreReposLink" class="center gt-py-3 gt-border-secondary-top">
@@ -394,7 +396,14 @@ const sfc = {
       }
 
       if (searchedURL === this.searchURL) {
-        this.repos = json.data.map((webSearchRepo) => {return {...webSearchRepo.repository, latest_commit_status_state: webSearchRepo.latest_commit_status.State}});
+        this.repos = json.data.map((webSearchRepo) => {
+          return {
+            ...webSearchRepo.repository,
+            latest_commit_status_state: webSearchRepo.latest_commit_status.State,
+            locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status,
+            latest_commit_status_state_link: webSearchRepo.latest_commit_status.TargetURL
+          };
+        });
         const count = response.headers.get('X-Total-Count');
         if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
           this.reposTotalCount = count;
@@ -494,8 +503,6 @@ ul li:not(:last-child) {
 }
 
 .repo-list-link {
-  padding: 6px 0;
-  gap: 6px;
   min-width: 0; /* for text truncation */
 }