diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue
index 1955decc1..51a774543 100644
--- a/web_src/js/components/ActionRunStatus.vue
+++ b/web_src/js/components/ActionRunStatus.vue
@@ -2,17 +2,6 @@
     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 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-if="['failure', 'cancelled', 'unknown'].includes(status)"/>
-  </span>
-</template>
-
 <script>
 import {SvgIcon} from '../svg.js';
 
@@ -38,3 +27,13 @@ export default {
   },
 };
 </script>
+<template>
+  <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-if="['failure', 'cancelled', 'unknown'].includes(status)"/>
+  </span>
+</template>
diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue
index 1b083ed13..96a6e6801 100644
--- a/web_src/js/components/ActivityHeatmap.vue
+++ b/web_src/js/components/ActivityHeatmap.vue
@@ -1,17 +1,3 @@
-<template>
-  <div class="total-contributions">
-    {{ locale.contributions_in_the_last_12_months }}
-  </div>
-  <calendar-heatmap
-    :locale="locale"
-    :no-data-text="locale.no_contributions"
-    :tooltip-unit="locale.contributions"
-    :end-date="endDate"
-    :values="values"
-    :range-color="colorRange"
-    @day-click="handleDayClick($event)"
-  />
-</template>
 <script>
 import {CalendarHeatmap} from 'vue3-calendar-heatmap';
 
@@ -67,3 +53,17 @@ export default {
   },
 };
 </script>
+<template>
+  <div class="total-contributions">
+    {{ locale.contributions_in_the_last_12_months }}
+  </div>
+  <calendar-heatmap
+    :locale="locale"
+    :no-data-text="locale.no_contributions"
+    :tooltip-unit="locale.contributions"
+    :end-date="endDate"
+    :values="values"
+    :range-color="colorRange"
+    @day-click="handleDayClick($event)"
+  />
+</template>
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index b6852655f..303e6d0c8 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -1,28 +1,3 @@
-<template>
-  <div ref="root">
-    <div v-if="loading" class="ui active centered inline loader"/>
-    <div v-if="!loading && issue !== null">
-      <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
-      <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p>
-      <p>{{ body }}</p>
-      <div>
-        <div
-          v-for="label in labels"
-          :key="label.name"
-          class="ui label"
-          :style="{ color: label.textColor, backgroundColor: label.color }"
-        >
-          {{ label.name }}
-        </div>
-      </div>
-    </div>
-    <div v-if="!loading && issue === null">
-      <p><small>{{ i18nErrorOccurred }}</small></p>
-      <p>{{ i18nErrorMessage }}</p>
-    </div>
-  </div>
-</template>
-
 <script>
 import $ from 'jquery';
 import {SvgIcon} from '../svg.js';
@@ -115,3 +90,27 @@ export default {
   }
 };
 </script>
+<template>
+  <div ref="root">
+    <div v-if="loading" class="ui active centered inline loader"/>
+    <div v-if="!loading && issue !== null">
+      <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
+      <p><svg-icon :name="icon" :class="['text', color]"/> <strong>{{ issue.title }}</strong> #{{ issue.number }}</p>
+      <p>{{ body }}</p>
+      <div>
+        <div
+          v-for="label in labels"
+          :key="label.name"
+          class="ui label"
+          :style="{ color: label.textColor, backgroundColor: label.color }"
+        >
+          {{ label.name }}
+        </div>
+      </div>
+    </div>
+    <div v-if="!loading && issue === null">
+      <p><small>{{ i18nErrorOccurred }}</small></p>
+      <p>{{ i18nErrorMessage }}</p>
+    </div>
+  </div>
+</template>
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 898362776..5b8075f07 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -1,152 +1,3 @@
-<template>
-  <div>
-    <div v-if="!isOrganization" class="ui two item menu">
-      <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{ textRepository }}</a>
-      <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a>
-    </div>
-    <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
-      <h4 class="ui top attached header gt-df gt-ac">
-        <div class="gt-f1 gt-df gt-ac">
-          {{ textMyRepos }}
-          <span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
-        </div>
-        <a class="gt-df gt-ac muted" :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo">
-          <svg-icon name="octicon-plus"/>
-        </a>
-      </h4>
-      <div class="ui attached segment repos-search">
-        <div class="ui fluid action left icon input" :class="{loading: isLoading}">
-          <input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos">
-          <i class="icon"><svg-icon name="octicon-search" :size="16"/></i>
-          <div class="ui dropdown icon button" :title="textFilter">
-            <svg-icon name="octicon-filter" :size="16"/>
-            <div class="menu">
-              <a class="item" @click="toggleArchivedFilter()">
-                <div class="ui checkbox" ref="checkboxArchivedFilter" :title="checkboxArchivedFilterTitle">
-                  <!--the "hidden" is necessary to make the checkbox work without Fomantic UI js,
-                      otherwise if the "input" handles click event for intermediate status, it breaks the internal state-->
-                  <input type="checkbox" class="hidden" v-bind.prop="checkboxArchivedFilterProps">
-                  <label>
-                    <svg-icon name="octicon-archive" :size="16" class-name="gt-mr-2"/>
-                    {{ textShowArchived }}
-                  </label>
-                </div>
-              </a>
-              <a class="item" @click="togglePrivateFilter()">
-                <div class="ui checkbox" ref="checkboxPrivateFilter" :title="checkboxPrivateFilterTitle">
-                  <input type="checkbox" class="hidden" v-bind.prop="checkboxPrivateFilterProps">
-                  <label>
-                    <svg-icon name="octicon-lock" :size="16" class-name="gt-mr-2"/>
-                    {{ textShowPrivate }}
-                  </label>
-                </div>
-              </a>
-            </div>
-          </div>
-        </div>
-        <div class="ui secondary tiny pointing borderless menu center grid repos-filter">
-          <a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
-            {{ textAll }}
-            <div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
-          </a>
-          <a class="item" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')">
-            {{ textSources }}
-            <div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
-          </a>
-          <a class="item" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')">
-            {{ textForks }}
-            <div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
-          </a>
-          <a class="item" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled">
-            {{ textMirrors }}
-            <div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
-          </a>
-          <a class="item" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')">
-            {{ textCollaborative }}
-            <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
-          </a>
-        </div>
-      </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 gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
-            <a class="repo-list-link muted" :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>
-            <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">
-          <div class="ui borderless pagination menu narrow">
-            <a
-              class="item navigation gt-py-2" :class="{'disabled': page === 1}"
-              @click="changePage(1)" :title="textFirstPage"
-            >
-              <svg-icon name="gitea-double-chevron-left" :size="16" class-name="gt-mr-2"/>
-            </a>
-            <a
-              class="item navigation gt-py-2" :class="{'disabled': page === 1}"
-              @click="changePage(page - 1)" :title="textPreviousPage"
-            >
-              <svg-icon name="octicon-chevron-left" :size="16" clsas-name="gt-mr-2"/>
-            </a>
-            <a class="active item gt-py-2">{{ page }}</a>
-            <a
-              class="item navigation" :class="{'disabled': page === finalPage}"
-              @click="changePage(page + 1)" :title="textNextPage"
-            >
-              <svg-icon name="octicon-chevron-right" :size="16" class-name="gt-ml-2"/>
-            </a>
-            <a
-              class="item navigation gt-py-2" :class="{'disabled': page === finalPage}"
-              @click="changePage(finalPage)" :title="textLastPage"
-            >
-              <svg-icon name="gitea-double-chevron-right" :size="16" class-name="gt-ml-2"/>
-            </a>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs">
-      <h4 class="ui top attached header gt-df gt-ac">
-        <div class="gt-f1 gt-df gt-ac">
-          {{ textMyOrgs }}
-          <span class="ui grey label gt-ml-3">{{ organizationsTotalCount }}</span>
-        </div>
-        <a class="gt-df gt-ac muted" v-if="canCreateOrganization" :href="subUrl + '/org/create'" :data-tooltip-content="textNewOrg">
-          <svg-icon name="octicon-plus"/>
-        </a>
-      </h4>
-      <div v-if="organizations.length" class="ui attached table segment gt-rounded-bottom">
-        <ul class="repo-owner-name-list">
-          <li class="gt-df gt-ac gt-py-3" v-for="org in organizations" :key="org.name">
-            <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)">
-              <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/>
-              <div class="text truncate">{{ org.name }}</div>
-              <div><!-- div to prevent underline of label on hover -->
-                <span class="ui tiny basic label" v-if="org.org_visibility !== 'public'">
-                  {{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }}
-                </span>
-              </div>
-            </a>
-            <div class="text light grey gt-df gt-ac gt-ml-3">
-              {{ org.num_repos }}
-              <svg-icon name="octicon-repo" :size="16" class-name="gt-ml-2 gt-mt-1"/>
-            </div>
-          </li>
-        </ul>
-      </div>
-    </div>
-  </div>
-</template>
-
 <script>
 import {createApp, nextTick} from 'vue';
 import $ from 'jquery';
@@ -485,8 +336,155 @@ export function initDashboardRepoList() {
 }
 
 export default sfc; // activate the IDE's Vue plugin
-
 </script>
+<template>
+  <div>
+    <div v-if="!isOrganization" class="ui two item menu">
+      <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{ textRepository }}</a>
+      <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a>
+    </div>
+    <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos">
+      <h4 class="ui top attached header gt-df gt-ac">
+        <div class="gt-f1 gt-df gt-ac">
+          {{ textMyRepos }}
+          <span class="ui grey label gt-ml-3">{{ reposTotalCount }}</span>
+        </div>
+        <a class="gt-df gt-ac muted" :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo">
+          <svg-icon name="octicon-plus"/>
+        </a>
+      </h4>
+      <div class="ui attached segment repos-search">
+        <div class="ui fluid action left icon input" :class="{loading: isLoading}">
+          <input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos">
+          <i class="icon"><svg-icon name="octicon-search" :size="16"/></i>
+          <div class="ui dropdown icon button" :title="textFilter">
+            <svg-icon name="octicon-filter" :size="16"/>
+            <div class="menu">
+              <a class="item" @click="toggleArchivedFilter()">
+                <div class="ui checkbox" ref="checkboxArchivedFilter" :title="checkboxArchivedFilterTitle">
+                  <!--the "hidden" is necessary to make the checkbox work without Fomantic UI js,
+                      otherwise if the "input" handles click event for intermediate status, it breaks the internal state-->
+                  <input type="checkbox" class="hidden" v-bind.prop="checkboxArchivedFilterProps">
+                  <label>
+                    <svg-icon name="octicon-archive" :size="16" class-name="gt-mr-2"/>
+                    {{ textShowArchived }}
+                  </label>
+                </div>
+              </a>
+              <a class="item" @click="togglePrivateFilter()">
+                <div class="ui checkbox" ref="checkboxPrivateFilter" :title="checkboxPrivateFilterTitle">
+                  <input type="checkbox" class="hidden" v-bind.prop="checkboxPrivateFilterProps">
+                  <label>
+                    <svg-icon name="octicon-lock" :size="16" class-name="gt-mr-2"/>
+                    {{ textShowPrivate }}
+                  </label>
+                </div>
+              </a>
+            </div>
+          </div>
+        </div>
+        <div class="ui secondary tiny pointing borderless menu center grid repos-filter">
+          <a class="item" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
+            {{ textAll }}
+            <div v-show="reposFilter === 'all'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
+          </a>
+          <a class="item" :class="{active: reposFilter === 'sources'}" @click="changeReposFilter('sources')">
+            {{ textSources }}
+            <div v-show="reposFilter === 'sources'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
+          </a>
+          <a class="item" :class="{active: reposFilter === 'forks'}" @click="changeReposFilter('forks')">
+            {{ textForks }}
+            <div v-show="reposFilter === 'forks'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
+          </a>
+          <a class="item" :class="{active: reposFilter === 'mirrors'}" @click="changeReposFilter('mirrors')" v-if="isMirrorsEnabled">
+            {{ textMirrors }}
+            <div v-show="reposFilter === 'mirrors'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
+          </a>
+          <a class="item" :class="{active: reposFilter === 'collaborative'}" @click="changeReposFilter('collaborative')">
+            {{ textCollaborative }}
+            <div v-show="reposFilter === 'collaborative'" class="ui circular mini grey label">{{ repoTypeCount }}</div>
+          </a>
+        </div>
+      </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 gt-py-3" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
+            <a class="repo-list-link muted" :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>
+            <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">
+          <div class="ui borderless pagination menu narrow">
+            <a
+              class="item navigation gt-py-2" :class="{'disabled': page === 1}"
+              @click="changePage(1)" :title="textFirstPage"
+            >
+              <svg-icon name="gitea-double-chevron-left" :size="16" class-name="gt-mr-2"/>
+            </a>
+            <a
+              class="item navigation gt-py-2" :class="{'disabled': page === 1}"
+              @click="changePage(page - 1)" :title="textPreviousPage"
+            >
+              <svg-icon name="octicon-chevron-left" :size="16" clsas-name="gt-mr-2"/>
+            </a>
+            <a class="active item gt-py-2">{{ page }}</a>
+            <a
+              class="item navigation" :class="{'disabled': page === finalPage}"
+              @click="changePage(page + 1)" :title="textNextPage"
+            >
+              <svg-icon name="octicon-chevron-right" :size="16" class-name="gt-ml-2"/>
+            </a>
+            <a
+              class="item navigation gt-py-2" :class="{'disabled': page === finalPage}"
+              @click="changePage(finalPage)" :title="textLastPage"
+            >
+              <svg-icon name="gitea-double-chevron-right" :size="16" class-name="gt-ml-2"/>
+            </a>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div v-if="!isOrganization" v-show="tab === 'organizations'" class="ui tab active list dashboard-orgs">
+      <h4 class="ui top attached header gt-df gt-ac">
+        <div class="gt-f1 gt-df gt-ac">
+          {{ textMyOrgs }}
+          <span class="ui grey label gt-ml-3">{{ organizationsTotalCount }}</span>
+        </div>
+        <a class="gt-df gt-ac muted" v-if="canCreateOrganization" :href="subUrl + '/org/create'" :data-tooltip-content="textNewOrg">
+          <svg-icon name="octicon-plus"/>
+        </a>
+      </h4>
+      <div v-if="organizations.length" class="ui attached table segment gt-rounded-bottom">
+        <ul class="repo-owner-name-list">
+          <li class="gt-df gt-ac gt-py-3" v-for="org in organizations" :key="org.name">
+            <a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)">
+              <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/>
+              <div class="text truncate">{{ org.name }}</div>
+              <div><!-- div to prevent underline of label on hover -->
+                <span class="ui tiny basic label" v-if="org.org_visibility !== 'public'">
+                  {{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }}
+                </span>
+              </div>
+            </a>
+            <div class="text light grey gt-df gt-ac gt-ml-3">
+              {{ org.num_repos }}
+              <svg-icon name="octicon-repo" :size="16" class-name="gt-ml-2 gt-mt-1"/>
+            </div>
+          </li>
+        </ul>
+      </div>
+    </div>
+  </div>
+</template>
 <style scoped>
 ul {
   list-style: none;
diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue
index 283ef03ab..48dc9d72f 100644
--- a/web_src/js/components/DiffCommitSelector.vue
+++ b/web_src/js/components/DiffCommitSelector.vue
@@ -1,75 +1,3 @@
-<template>
-  <div class="ui scrolling dropdown custom">
-    <button
-      class="ui basic button"
-      id="diff-commit-list-expand"
-      @click.stop="toggleMenu()"
-      :data-tooltip-content="locale.filter_changes_by_commit"
-      aria-haspopup="true"
-      aria-controls="diff-commit-selector-menu"
-      :aria-label="locale.filter_changes_by_commit"
-      aria-activedescendant="diff-commit-list-show-all"
-    >
-      <svg-icon name="octicon-git-commit"/>
-    </button>
-    <div class="menu left transition" id="diff-commit-selector-menu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'">
-      <div class="loading-indicator is-loading" v-if="isLoading"/>
-      <div v-if="!isLoading" class="vertical item gt-df gt-fc gt-gap-2" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()">
-        <div class="gt-ellipsis">
-          {{ locale.show_all_commits }}
-        </div>
-        <div class="gt-ellipsis text light-2 gt-mb-0">
-          {{ locale.stats_num_commits }}
-        </div>
-      </div>
-      <!-- only show the show changes since last review if there is a review AND we are commits ahead of the last review -->
-      <div
-        v-if="lastReviewCommitSha != null" role="menuitem"
-        class="vertical item gt-df gt-fc gt-gap-2 gt-border-secondary-top"
-        :class="{disabled: commitsSinceLastReview === 0}"
-        @keydown.enter="changesSinceLastReviewClick()"
-        @click="changesSinceLastReviewClick()"
-      >
-        <div class="gt-ellipsis">
-          {{ locale.show_changes_since_your_last_review }}
-        </div>
-        <div class="gt-ellipsis text light-2">
-          {{ commitsSinceLastReview }} commits
-        </div>
-      </div>
-      <span v-if="!isLoading" class="info gt-border-secondary-top text light-2">{{ locale.select_commit_hold_shift_for_range }}</span>
-      <template v-for="commit in commits" :key="commit.id">
-        <div
-          class="vertical item gt-df gt-gap-2 gt-border-secondary-top" role="menuitem"
-          :class="{selection: commit.selected, hovered: commit.hovered}"
-          @keydown.enter.exact="commitClicked(commit.id)"
-          @keydown.enter.shift.exact="commitClickedShift(commit)"
-          @mouseover.shift="highlight(commit)"
-          @click.exact="commitClicked(commit.id)"
-          @click.ctrl.exact="commitClicked(commit.id, true)"
-          @click.meta.exact="commitClicked(commit.id, true)"
-          @click.shift.exact.stop.prevent="commitClickedShift(commit)"
-        >
-          <div class="gt-f1 gt-df gt-fc gt-gap-2">
-            <div class="gt-ellipsis commit-list-summary">
-              {{ commit.summary }}
-            </div>
-            <div class="gt-ellipsis text light-2">
-              {{ commit.committer_or_author_name }}
-              <span class="text right">
-                <relative-time class="time-since" prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time>
-              </span>
-            </div>
-          </div>
-          <div class="gt-mono">
-            {{ commit.short_sha }}
-          </div>
-        </div>
-      </template>
-    </div>
-  </div>
-</template>
-
 <script>
 import {SvgIcon} from '../svg.js';
 
@@ -259,6 +187,77 @@ export default {
   }
 };
 </script>
+<template>
+  <div class="ui scrolling dropdown custom">
+    <button
+      class="ui basic button"
+      id="diff-commit-list-expand"
+      @click.stop="toggleMenu()"
+      :data-tooltip-content="locale.filter_changes_by_commit"
+      aria-haspopup="true"
+      aria-controls="diff-commit-selector-menu"
+      :aria-label="locale.filter_changes_by_commit"
+      aria-activedescendant="diff-commit-list-show-all"
+    >
+      <svg-icon name="octicon-git-commit"/>
+    </button>
+    <div class="menu left transition" id="diff-commit-selector-menu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'">
+      <div class="loading-indicator is-loading" v-if="isLoading"/>
+      <div v-if="!isLoading" class="vertical item gt-df gt-fc gt-gap-2" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()">
+        <div class="gt-ellipsis">
+          {{ locale.show_all_commits }}
+        </div>
+        <div class="gt-ellipsis text light-2 gt-mb-0">
+          {{ locale.stats_num_commits }}
+        </div>
+      </div>
+      <!-- only show the show changes since last review if there is a review AND we are commits ahead of the last review -->
+      <div
+        v-if="lastReviewCommitSha != null" role="menuitem"
+        class="vertical item gt-df gt-fc gt-gap-2 gt-border-secondary-top"
+        :class="{disabled: commitsSinceLastReview === 0}"
+        @keydown.enter="changesSinceLastReviewClick()"
+        @click="changesSinceLastReviewClick()"
+      >
+        <div class="gt-ellipsis">
+          {{ locale.show_changes_since_your_last_review }}
+        </div>
+        <div class="gt-ellipsis text light-2">
+          {{ commitsSinceLastReview }} commits
+        </div>
+      </div>
+      <span v-if="!isLoading" class="info gt-border-secondary-top text light-2">{{ locale.select_commit_hold_shift_for_range }}</span>
+      <template v-for="commit in commits" :key="commit.id">
+        <div
+          class="vertical item gt-df gt-gap-2 gt-border-secondary-top" role="menuitem"
+          :class="{selection: commit.selected, hovered: commit.hovered}"
+          @keydown.enter.exact="commitClicked(commit.id)"
+          @keydown.enter.shift.exact="commitClickedShift(commit)"
+          @mouseover.shift="highlight(commit)"
+          @click.exact="commitClicked(commit.id)"
+          @click.ctrl.exact="commitClicked(commit.id, true)"
+          @click.meta.exact="commitClicked(commit.id, true)"
+          @click.shift.exact.stop.prevent="commitClickedShift(commit)"
+        >
+          <div class="gt-f1 gt-df gt-fc gt-gap-2">
+            <div class="gt-ellipsis commit-list-summary">
+              {{ commit.summary }}
+            </div>
+            <div class="gt-ellipsis text light-2">
+              {{ commit.committer_or_author_name }}
+              <span class="text right">
+                <relative-time class="time-since" prefix="" :datetime="commit.time" data-tooltip-content data-tooltip-interactive="true">{{ commit.time }}</relative-time>
+              </span>
+            </div>
+          </div>
+          <div class="gt-mono">
+            {{ commit.short_sha }}
+          </div>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
 <style scoped>
   .hovered:not(.selection) {
     background-color: var(--color-small-accent) !important;
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
index 131be0181..103526d02 100644
--- a/web_src/js/components/DiffFileList.vue
+++ b/web_src/js/components/DiffFileList.vue
@@ -1,25 +1,3 @@
-<template>
-  <ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible">
-    <li v-for="file in store.files" :key="file.NameHash">
-      <div class="gt-font-semibold gt-df gt-ac pull-right">
-        <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span>
-        {{ file.IsBin ? '' : file.Addition + file.Deletion }}
-        <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
-          <div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }"/>
-        </span>
-      </div>
-      <!-- todo finish all file status, now modify, add, delete and rename -->
-      <span :class="['status', diffTypeToString(file.Type)]" :data-tooltip-content="diffTypeToString(file.Type)">&nbsp;</span>
-      <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
-    </li>
-    <li v-if="store.isIncomplete" class="gt-pt-2">
-      <span class="file gt-df gt-ac gt-sb">{{ store.tooManyFilesMessage }}
-        <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
-      </span>
-    </li>
-  </ol>
-</template>
-
 <script>
 import {loadMoreFiles} from '../features/repo-diff.js';
 import {diffTreeStore} from '../modules/stores.js';
@@ -57,3 +35,24 @@ export default {
   },
 };
 </script>
+<template>
+  <ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible">
+    <li v-for="file in store.files" :key="file.NameHash">
+      <div class="gt-font-semibold gt-df gt-ac pull-right">
+        <span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span>
+        {{ file.IsBin ? '' : file.Addition + file.Deletion }}
+        <span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
+          <div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }"/>
+        </span>
+      </div>
+      <!-- todo finish all file status, now modify, add, delete and rename -->
+      <span :class="['status', diffTypeToString(file.Type)]" :data-tooltip-content="diffTypeToString(file.Type)">&nbsp;</span>
+      <a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
+    </li>
+    <li v-if="store.isIncomplete" class="gt-pt-2">
+      <span class="file gt-df gt-ac gt-sb">{{ store.tooManyFilesMessage }}
+        <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
+      </span>
+    </li>
+  </ol>
+</template>
diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue
index e02222bde..8ee86857a 100644
--- a/web_src/js/components/DiffFileTree.vue
+++ b/web_src/js/components/DiffFileTree.vue
@@ -1,13 +1,3 @@
-<template>
-  <div v-if="store.fileTreeIsVisible" class="gt-mr-3 gt-mt-3 diff-detail-box">
-    <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
-    <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
-    <div v-if="store.isIncomplete" class="gt-pt-2">
-      <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
-    </div>
-  </div>
-</template>
-
 <script>
 import DiffFileTreeItem from './DiffFileTreeItem.vue';
 import {loadMoreFiles} from '../features/repo-diff.js';
@@ -135,3 +125,12 @@ export default {
   },
 };
 </script>
+<template>
+  <div v-if="store.fileTreeIsVisible" class="gt-mr-3 gt-mt-3 diff-detail-box">
+    <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
+    <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
+    <div v-if="store.isIncomplete" class="gt-pt-2">
+      <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
+    </div>
+  </div>
+</template>
diff --git a/web_src/js/components/DiffFileTreeItem.vue b/web_src/js/components/DiffFileTreeItem.vue
index d21afc42c..553ab1464 100644
--- a/web_src/js/components/DiffFileTreeItem.vue
+++ b/web_src/js/components/DiffFileTreeItem.vue
@@ -1,27 +1,3 @@
-<template>
-  <!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"-->
-  <a
-    v-if="item.isFile" class="item-file"
-    :class="{'selected': store.selectedItem === '#diff-' + item.file.NameHash, 'viewed': item.file.IsViewed}"
-    :title="item.name" :href="'#diff-' + item.file.NameHash"
-  >
-    <!-- file -->
-    <SvgIcon name="octicon-file"/>
-    <span class="gt-ellipsis gt-f1">{{ item.name }}</span>
-    <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/>
-  </a>
-  <div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed">
-    <!-- directory -->
-    <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/>
-    <SvgIcon class="text primary" name="octicon-file-directory-fill"/>
-    <span class="gt-ellipsis">{{ item.name }}</span>
-  </div>
-
-  <div v-if="item.children?.length" v-show="!collapsed" class="sub-items">
-    <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem"/>
-  </div>
-</template>
-
 <script>
 import {SvgIcon} from '../svg.js';
 import {diffTreeStore} from '../modules/stores.js';
@@ -52,7 +28,29 @@ export default {
   },
 };
 </script>
+<template>
+  <!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"-->
+  <a
+    v-if="item.isFile" class="item-file"
+    :class="{'selected': store.selectedItem === '#diff-' + item.file.NameHash, 'viewed': item.file.IsViewed}"
+    :title="item.name" :href="'#diff-' + item.file.NameHash"
+  >
+    <!-- file -->
+    <SvgIcon name="octicon-file"/>
+    <span class="gt-ellipsis gt-f1">{{ item.name }}</span>
+    <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/>
+  </a>
+  <div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed">
+    <!-- directory -->
+    <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/>
+    <SvgIcon class="text primary" name="octicon-file-directory-fill"/>
+    <span class="gt-ellipsis">{{ item.name }}</span>
+  </div>
 
+  <div v-if="item.children?.length" v-show="!collapsed" class="sub-items">
+    <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem"/>
+  </div>
+</template>
 <style scoped>
 a, a:hover {
   text-decoration: none;
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue
index 673773a91..cca8b9dd6 100644
--- a/web_src/js/components/PullRequestMergeForm.vue
+++ b/web_src/js/components/PullRequestMergeForm.vue
@@ -1,3 +1,80 @@
+<script>
+import {SvgIcon} from '../svg.js';
+
+const {csrfToken, pageData} = window.config;
+
+export default {
+  components: {SvgIcon},
+  data: () => ({
+    csrfToken,
+    mergeForm: pageData.pullRequestMergeForm,
+
+    mergeTitleFieldValue: '',
+    mergeMessageFieldValue: '',
+    deleteBranchAfterMerge: false,
+    autoMergeWhenSucceed: false,
+
+    mergeStyle: '',
+    mergeStyleDetail: { // dummy only, these values will come from one of the mergeForm.mergeStyles
+      hideMergeMessageTexts: false,
+      textDoMerge: '',
+      mergeTitleFieldText: '',
+      mergeMessageFieldText: '',
+      hideAutoMerge: false,
+    },
+    mergeStyleAllowedCount: 0,
+
+    showMergeStyleMenu: false,
+    showActionForm: false,
+  }),
+  computed: {
+    mergeButtonStyleClass() {
+      if (this.mergeForm.allOverridableChecksOk) return 'green';
+      return this.autoMergeWhenSucceed ? 'blue' : 'red';
+    },
+    forceMerge() {
+      return this.mergeForm.canMergeNow && !this.mergeForm.allOverridableChecksOk;
+    },
+  },
+  watch: {
+    mergeStyle(val) {
+      this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val);
+    }
+  },
+  created() {
+    this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0);
+
+    let mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed && e.name === this.mergeForm.defaultMergeStyle)?.name;
+    if (!mergeStyle) mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name;
+    this.switchMergeStyle(mergeStyle, !this.mergeForm.canMergeNow);
+  },
+  mounted() {
+    document.addEventListener('mouseup', this.hideMergeStyleMenu);
+  },
+  unmounted() {
+    document.removeEventListener('mouseup', this.hideMergeStyleMenu);
+  },
+  methods: {
+    hideMergeStyleMenu() {
+      this.showMergeStyleMenu = false;
+    },
+    toggleActionForm(show) {
+      this.showActionForm = show;
+      if (!show) return;
+      this.deleteBranchAfterMerge = this.mergeForm.defaultDeleteBranchAfterMerge;
+      this.mergeTitleFieldValue = this.mergeStyleDetail.mergeTitleFieldText;
+      this.mergeMessageFieldValue = this.mergeStyleDetail.mergeMessageFieldText;
+    },
+    switchMergeStyle(name, autoMerge = false) {
+      this.mergeStyle = name;
+      this.autoMergeWhenSucceed = autoMerge;
+    },
+    clearMergeMessage() {
+      this.mergeMessageFieldValue = this.mergeForm.defaultMergeMessage;
+    },
+  },
+};
+</script>
 <template>
   <!--
   if this component is shown, either the user is an admin (can do a merge without checks), or they are a writer who has the permission to do a merge
@@ -106,85 +183,6 @@
     </div>
   </div>
 </template>
-
-<script>
-import {SvgIcon} from '../svg.js';
-
-const {csrfToken, pageData} = window.config;
-
-export default {
-  components: {SvgIcon},
-  data: () => ({
-    csrfToken,
-    mergeForm: pageData.pullRequestMergeForm,
-
-    mergeTitleFieldValue: '',
-    mergeMessageFieldValue: '',
-    deleteBranchAfterMerge: false,
-    autoMergeWhenSucceed: false,
-
-    mergeStyle: '',
-    mergeStyleDetail: { // dummy only, these values will come from one of the mergeForm.mergeStyles
-      hideMergeMessageTexts: false,
-      textDoMerge: '',
-      mergeTitleFieldText: '',
-      mergeMessageFieldText: '',
-      hideAutoMerge: false,
-    },
-    mergeStyleAllowedCount: 0,
-
-    showMergeStyleMenu: false,
-    showActionForm: false,
-  }),
-  computed: {
-    mergeButtonStyleClass() {
-      if (this.mergeForm.allOverridableChecksOk) return 'green';
-      return this.autoMergeWhenSucceed ? 'blue' : 'red';
-    },
-    forceMerge() {
-      return this.mergeForm.canMergeNow && !this.mergeForm.allOverridableChecksOk;
-    },
-  },
-  watch: {
-    mergeStyle(val) {
-      this.mergeStyleDetail = this.mergeForm.mergeStyles.find((e) => e.name === val);
-    }
-  },
-  created() {
-    this.mergeStyleAllowedCount = this.mergeForm.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0);
-
-    let mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed && e.name === this.mergeForm.defaultMergeStyle)?.name;
-    if (!mergeStyle) mergeStyle = this.mergeForm.mergeStyles.find((e) => e.allowed)?.name;
-    this.switchMergeStyle(mergeStyle, !this.mergeForm.canMergeNow);
-  },
-  mounted() {
-    document.addEventListener('mouseup', this.hideMergeStyleMenu);
-  },
-  unmounted() {
-    document.removeEventListener('mouseup', this.hideMergeStyleMenu);
-  },
-  methods: {
-    hideMergeStyleMenu() {
-      this.showMergeStyleMenu = false;
-    },
-    toggleActionForm(show) {
-      this.showActionForm = show;
-      if (!show) return;
-      this.deleteBranchAfterMerge = this.mergeForm.defaultDeleteBranchAfterMerge;
-      this.mergeTitleFieldValue = this.mergeStyleDetail.mergeTitleFieldText;
-      this.mergeMessageFieldValue = this.mergeStyleDetail.mergeMessageFieldText;
-    },
-    switchMergeStyle(name, autoMerge = false) {
-      this.mergeStyle = name;
-      this.autoMergeWhenSucceed = autoMerge;
-    },
-    clearMergeMessage() {
-      this.mergeMessageFieldValue = this.mergeForm.defaultMergeMessage;
-    },
-  },
-};
-</script>
-
 <style scoped>
 /* to keep UI the same, at the moment we are still using some Fomantic UI styles, but we do not use their scripts, so we need to fine tune some styles */
 .ui.dropdown .menu.show {
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 8d18ad230..925ff7e08 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -1,124 +1,3 @@
-<template>
-  <div class="ui container action-view-container">
-    <div class="action-view-header">
-      <div class="action-info-summary">
-        <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">
-          {{ locale.approve }}
-        </button>
-        <button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel">
-          {{ locale.cancel }}
-        </button>
-        <button class="ui basic small compact button gt-mr-0 link-action" :data-url="`${run.link}/rerun`" v-else-if="run.canRerun">
-          {{ locale.rerun_all }}
-        </button>
-      </div>
-      <div class="action-commit-summary">
-        {{ run.commit.localeCommit }}
-        <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a>
-        {{ run.commit.localePushedBy }}
-        <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
-        <span class="ui label" v-if="run.commit.shortSHA">
-          <a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
-        </span>
-      </div>
-    </div>
-    <div class="action-view-body">
-      <div class="action-view-left">
-        <div class="job-group-section">
-          <div class="job-brief-list">
-            <a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1">
-              <div class="job-brief-item-left">
-                <ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
-                <span class="job-brief-name gt-mx-3 gt-ellipsis">{{ job.name }}</span>
-              </div>
-              <span class="job-brief-item-right">
-                <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun gt-mx-3 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/>
-                <span class="step-summary-duration">{{ job.duration }}</span>
-              </span>
-            </a>
-          </div>
-        </div>
-        <div class="job-artifacts" v-if="artifacts.length > 0">
-          <div class="job-artifacts-title">
-            {{ locale.artifactsTitle }}
-          </div>
-          <ul class="job-artifacts-list">
-            <li class="job-artifacts-item" v-for="artifact in artifacts" :key="artifact.name">
-              <a class="job-artifacts-link" target="_blank" :href="run.link+'/artifacts/'+artifact.name">
-                <SvgIcon name="octicon-file" class="ui text black job-artifacts-icon"/>{{ artifact.name }}
-              </a>
-            </li>
-          </ul>
-        </div>
-      </div>
-
-      <div class="action-view-right">
-        <div class="job-info-header">
-          <div class="job-info-header-left">
-            <h3 class="job-info-header-title">
-              {{ currentJob.title }}
-            </h3>
-            <p class="job-info-header-detail">
-              {{ currentJob.detail }}
-            </p>
-          </div>
-          <div class="job-info-header-right">
-            <div class="ui top right pointing dropdown custom jump item" @click.stop="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
-              <button class="btn gt-interact-bg gt-p-3">
-                <SvgIcon name="octicon-gear" :size="18"/>
-              </button>
-              <div class="menu transition action-job-menu" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
-                <a class="item" @click="toggleTimeDisplay('seconds')">
-                  <i class="icon"><SvgIcon :name="timeVisible['log-time-seconds'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i>
-                  {{ locale.showLogSeconds }}
-                </a>
-                <a class="item" @click="toggleTimeDisplay('stamp')">
-                  <i class="icon"><SvgIcon :name="timeVisible['log-time-stamp'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i>
-                  {{ locale.showTimeStamps }}
-                </a>
-                <a class="item" @click="toggleFullScreen()">
-                  <i class="icon"><SvgIcon :name="isFullScreen ? 'octicon-check' : 'gitea-empty-checkbox'"/></i>
-                  {{ locale.showFullScreen }}
-                </a>
-                <div class="divider"/>
-                <a :class="['item', currentJob.steps.length === 0 ? 'disabled' : '']" :href="run.link+'/jobs/'+jobIndex+'/logs'" target="_blank">
-                  <i class="icon"><SvgIcon name="octicon-download"/></i>
-                  {{ locale.downloadLogs }}
-                </a>
-              </div>
-            </div>
-          </div>
-        </div>
-        <div class="job-step-container" ref="steps">
-          <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
-            <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 gt-ellipsis">{{ jobStep.summary }}</span>
-              <span class="step-summary-duration">{{ jobStep.duration }}</span>
-            </div>
-
-            <!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM,
-            use native DOM elements for "log line" to improve performance, Vue is not suitable for managing so many reactive elements. -->
-            <div class="job-step-logs" ref="logs" v-show="currentJobStepsStates[i].expanded"/>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
 <script>
 import {SvgIcon} from '../svg.js';
 import ActionRunStatus from './ActionRunStatus.vue';
@@ -472,9 +351,127 @@ export function initRepositoryActionView() {
   });
   view.mount(el);
 }
-
 </script>
+<template>
+  <div class="ui container action-view-container">
+    <div class="action-view-header">
+      <div class="action-info-summary">
+        <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">
+          {{ locale.approve }}
+        </button>
+        <button class="ui basic small compact button red" @click="cancelRun()" v-else-if="run.canCancel">
+          {{ locale.cancel }}
+        </button>
+        <button class="ui basic small compact button gt-mr-0 link-action" :data-url="`${run.link}/rerun`" v-else-if="run.canRerun">
+          {{ locale.rerun_all }}
+        </button>
+      </div>
+      <div class="action-commit-summary">
+        {{ run.commit.localeCommit }}
+        <a class="muted" :href="run.commit.link">{{ run.commit.shortSHA }}</a>
+        {{ run.commit.localePushedBy }}
+        <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
+        <span class="ui label" v-if="run.commit.shortSHA">
+          <a :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
+        </span>
+      </div>
+    </div>
+    <div class="action-view-body">
+      <div class="action-view-left">
+        <div class="job-group-section">
+          <div class="job-brief-list">
+            <a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1">
+              <div class="job-brief-item-left">
+                <ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
+                <span class="job-brief-name gt-mx-3 gt-ellipsis">{{ job.name }}</span>
+              </div>
+              <span class="job-brief-item-right">
+                <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun gt-mx-3 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/>
+                <span class="step-summary-duration">{{ job.duration }}</span>
+              </span>
+            </a>
+          </div>
+        </div>
+        <div class="job-artifacts" v-if="artifacts.length > 0">
+          <div class="job-artifacts-title">
+            {{ locale.artifactsTitle }}
+          </div>
+          <ul class="job-artifacts-list">
+            <li class="job-artifacts-item" v-for="artifact in artifacts" :key="artifact.name">
+              <a class="job-artifacts-link" target="_blank" :href="run.link+'/artifacts/'+artifact.name">
+                <SvgIcon name="octicon-file" class="ui text black job-artifacts-icon"/>{{ artifact.name }}
+              </a>
+            </li>
+          </ul>
+        </div>
+      </div>
 
+      <div class="action-view-right">
+        <div class="job-info-header">
+          <div class="job-info-header-left">
+            <h3 class="job-info-header-title">
+              {{ currentJob.title }}
+            </h3>
+            <p class="job-info-header-detail">
+              {{ currentJob.detail }}
+            </p>
+          </div>
+          <div class="job-info-header-right">
+            <div class="ui top right pointing dropdown custom jump item" @click.stop="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
+              <button class="btn gt-interact-bg gt-p-3">
+                <SvgIcon name="octicon-gear" :size="18"/>
+              </button>
+              <div class="menu transition action-job-menu" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
+                <a class="item" @click="toggleTimeDisplay('seconds')">
+                  <i class="icon"><SvgIcon :name="timeVisible['log-time-seconds'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i>
+                  {{ locale.showLogSeconds }}
+                </a>
+                <a class="item" @click="toggleTimeDisplay('stamp')">
+                  <i class="icon"><SvgIcon :name="timeVisible['log-time-stamp'] ? 'octicon-check' : 'gitea-empty-checkbox'"/></i>
+                  {{ locale.showTimeStamps }}
+                </a>
+                <a class="item" @click="toggleFullScreen()">
+                  <i class="icon"><SvgIcon :name="isFullScreen ? 'octicon-check' : 'gitea-empty-checkbox'"/></i>
+                  {{ locale.showFullScreen }}
+                </a>
+                <div class="divider"/>
+                <a :class="['item', currentJob.steps.length === 0 ? 'disabled' : '']" :href="run.link+'/jobs/'+jobIndex+'/logs'" target="_blank">
+                  <i class="icon"><SvgIcon name="octicon-download"/></i>
+                  {{ locale.downloadLogs }}
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="job-step-container" ref="steps">
+          <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
+            <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 gt-ellipsis">{{ jobStep.summary }}</span>
+              <span class="step-summary-duration">{{ jobStep.duration }}</span>
+            </div>
+
+            <!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM,
+            use native DOM elements for "log line" to improve performance, Vue is not suitable for managing so many reactive elements. -->
+            <div class="job-step-logs" ref="logs" v-show="currentJobStepsStates[i].expanded"/>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
 <style scoped>
 .action-view-body {
   padding-top: 12px;
diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue
index 294ee6f7b..fe41218d8 100644
--- a/web_src/js/components/RepoActivityTopAuthors.vue
+++ b/web_src/js/components/RepoActivityTopAuthors.vue
@@ -1,54 +1,3 @@
-<template>
-  <div>
-    <div class="activity-bar-graph" ref="style" style="width: 0; height: 0;"/>
-    <div class="activity-bar-graph-alt" ref="altStyle" style="width: 0; height: 0;"/>
-    <vue-bar-graph
-      :points="graphPoints"
-      :show-x-axis="true"
-      :show-y-axis="false"
-      :show-values="true"
-      :width="graphWidth"
-      :bar-color="colors.barColor"
-      :text-color="colors.textColor"
-      :text-alt-color="colors.textAltColor"
-      :height="100"
-      :label-height="20"
-    >
-      <template #label="opt">
-        <g v-for="(author, idx) in graphAuthors" :key="author.position">
-          <a
-            v-if="opt.bar.index === idx && author.home_link"
-            :href="author.home_link"
-          >
-            <image
-              :x="`${opt.bar.midPoint - 10}px`"
-              :y="`${opt.bar.yLabel}px`"
-              height="20"
-              width="20"
-              :href="author.avatar_link"
-            />
-          </a>
-          <image
-            v-else-if="opt.bar.index === idx"
-            :x="`${opt.bar.midPoint - 10}px`"
-            :y="`${opt.bar.yLabel}px`"
-            height="20"
-            width="20"
-            :href="author.avatar_link"
-          />
-        </g>
-      </template>
-      <template #title="opt">
-        <tspan v-for="(author, idx) in graphAuthors" :key="author.position">
-          <tspan v-if="opt.bar.index === idx">
-            {{ author.name }}
-          </tspan>
-        </tspan>
-      </template>
-    </vue-bar-graph>
-  </div>
-</template>
-
 <script>
 import VueBarGraph from 'vue-bar-graph';
 import {createApp} from 'vue';
@@ -110,3 +59,53 @@ export function initRepoActivityTopAuthorsChart() {
 
 export default sfc; // activate the IDE's Vue plugin
 </script>
+<template>
+  <div>
+    <div class="activity-bar-graph" ref="style" style="width: 0; height: 0;"/>
+    <div class="activity-bar-graph-alt" ref="altStyle" style="width: 0; height: 0;"/>
+    <vue-bar-graph
+      :points="graphPoints"
+      :show-x-axis="true"
+      :show-y-axis="false"
+      :show-values="true"
+      :width="graphWidth"
+      :bar-color="colors.barColor"
+      :text-color="colors.textColor"
+      :text-alt-color="colors.textAltColor"
+      :height="100"
+      :label-height="20"
+    >
+      <template #label="opt">
+        <g v-for="(author, idx) in graphAuthors" :key="author.position">
+          <a
+            v-if="opt.bar.index === idx && author.home_link"
+            :href="author.home_link"
+          >
+            <image
+              :x="`${opt.bar.midPoint - 10}px`"
+              :y="`${opt.bar.yLabel}px`"
+              height="20"
+              width="20"
+              :href="author.avatar_link"
+            />
+          </a>
+          <image
+            v-else-if="opt.bar.index === idx"
+            :x="`${opt.bar.midPoint - 10}px`"
+            :y="`${opt.bar.yLabel}px`"
+            height="20"
+            width="20"
+            :href="author.avatar_link"
+          />
+        </g>
+      </template>
+      <template #title="opt">
+        <tspan v-for="(author, idx) in graphAuthors" :key="author.position">
+          <tspan v-if="opt.bar.index === idx">
+            {{ author.name }}
+          </tspan>
+        </tspan>
+      </template>
+    </vue-bar-graph>
+  </div>
+</template>
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index 1aba3b293..b64b66d18 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -1,76 +1,3 @@
-<template>
-  <div class="ui dropdown custom">
-    <button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
-      <span class="text gt-df gt-ac gt-mr-2">
-        <template v-if="release">{{ textReleaseCompare }}</template>
-        <template v-else>
-          <svg-icon v-if="isViewTag" name="octicon-tag"/>
-          <svg-icon v-else name="octicon-git-branch"/>
-          <strong ref="dropdownRefName" class="gt-ml-3">{{ refNameText }}</strong>
-        </template>
-      </span>
-      <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>
-    </button>
-    <div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak>
-      <div class="ui icon search input">
-        <i class="icon"><svg-icon name="octicon-filter" :size="16"/></i>
-        <input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" :placeholder="searchFieldPlaceholder">
-      </div>
-      <div v-if="showBranchesInDropdown" class="branch-tag-tab">
-        <a class="branch-tag-item muted" :class="{active: mode === 'branches'}" href="#" @click="handleTabSwitch('branches')">
-          <svg-icon name="octicon-git-branch" :size="16" class-name="gt-mr-2"/>{{ textBranches }}
-        </a>
-        <a v-if="!noTag" class="branch-tag-item muted" :class="{active: mode === 'tags'}" href="#" @click="handleTabSwitch('tags')">
-          <svg-icon name="octicon-tag" :size="16" class-name="gt-mr-2"/>{{ textTags }}
-        </a>
-      </div>
-      <div class="branch-tag-divider"/>
-      <div class="scrolling menu" ref="scrollContainer">
-        <svg-icon name="octicon-rss" symbol-id="svg-symbol-octicon-rss"/>
-        <div class="loading-indicator is-loading" v-if="isLoading"/>
-        <div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index">
-          {{ item.name }}
-          <div class="ui label" v-if="item.name===defaultBranch && mode === 'branches'">
-            {{ textDefaultBranchLabel }}
-          </div>
-          <a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon gt-float-right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
-            <!-- creating a lot of Vue component is pretty slow, so we use a static SVG here -->
-            <svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg>
-          </a>
-        </div>
-        <div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
-          <a href="#" @click="createNewBranch()">
-            <div v-show="shouldCreateTag">
-              <i class="reference tags icon"/>
-              <!-- eslint-disable-next-line vue/no-v-html -->
-              <span v-html="textCreateTag.replace('%s', searchTerm)"/>
-            </div>
-            <div v-show="!shouldCreateTag">
-              <svg-icon name="octicon-git-branch"/>
-              <!-- eslint-disable-next-line vue/no-v-html -->
-              <span v-html="textCreateBranch.replace('%s', searchTerm)"/>
-            </div>
-            <div class="text small">
-              <span v-if="isViewBranch || release">{{ textCreateBranchFrom.replace('%s', branchName) }}</span>
-              <span v-else-if="isViewTag">{{ textCreateBranchFrom.replace('%s', tagName) }}</span>
-              <span v-else>{{ textCreateBranchFrom.replace('%s', commitIdShort) }}</span>
-            </div>
-          </a>
-          <form ref="newBranchForm" :action="formActionUrl" method="post">
-            <input type="hidden" name="_csrf" :value="csrfToken">
-            <input type="hidden" name="new_branch_name" v-model="searchTerm">
-            <input type="hidden" name="create_tag" v-model="shouldCreateTag">
-            <input type="hidden" name="current_path" v-model="treePath" v-if="treePath">
-          </form>
-        </div>
-      </div>
-      <div class="message" v-if="showNoResults && !isLoading">
-        {{ noResults }}
-      </div>
-    </div>
-  </div>
-</template>
-
 <script>
 import {createApp, nextTick} from 'vue';
 import $ from 'jquery';
@@ -317,7 +244,78 @@ export function initRepoBranchTagSelector(selector) {
 
 export default sfc; // activate IDE's Vue plugin
 </script>
-
+<template>
+  <div class="ui dropdown custom">
+    <button class="branch-dropdown-button gt-ellipsis ui basic small compact button gt-df gt-m-0" @click="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
+      <span class="text gt-df gt-ac gt-mr-2">
+        <template v-if="release">{{ textReleaseCompare }}</template>
+        <template v-else>
+          <svg-icon v-if="isViewTag" name="octicon-tag"/>
+          <svg-icon v-else name="octicon-git-branch"/>
+          <strong ref="dropdownRefName" class="gt-ml-3">{{ refNameText }}</strong>
+        </template>
+      </span>
+      <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>
+    </button>
+    <div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak>
+      <div class="ui icon search input">
+        <i class="icon"><svg-icon name="octicon-filter" :size="16"/></i>
+        <input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" :placeholder="searchFieldPlaceholder">
+      </div>
+      <div v-if="showBranchesInDropdown" class="branch-tag-tab">
+        <a class="branch-tag-item muted" :class="{active: mode === 'branches'}" href="#" @click="handleTabSwitch('branches')">
+          <svg-icon name="octicon-git-branch" :size="16" class-name="gt-mr-2"/>{{ textBranches }}
+        </a>
+        <a v-if="!noTag" class="branch-tag-item muted" :class="{active: mode === 'tags'}" href="#" @click="handleTabSwitch('tags')">
+          <svg-icon name="octicon-tag" :size="16" class-name="gt-mr-2"/>{{ textTags }}
+        </a>
+      </div>
+      <div class="branch-tag-divider"/>
+      <div class="scrolling menu" ref="scrollContainer">
+        <svg-icon name="octicon-rss" symbol-id="svg-symbol-octicon-rss"/>
+        <div class="loading-indicator is-loading" v-if="isLoading"/>
+        <div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index">
+          {{ item.name }}
+          <div class="ui label" v-if="item.name===defaultBranch && mode === 'branches'">
+            {{ textDefaultBranchLabel }}
+          </div>
+          <a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon gt-float-right" :href="rssURLPrefix + item.url" target="_blank" @click.stop>
+            <!-- creating a lot of Vue component is pretty slow, so we use a static SVG here -->
+            <svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg>
+          </a>
+        </div>
+        <div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length">
+          <a href="#" @click="createNewBranch()">
+            <div v-show="shouldCreateTag">
+              <i class="reference tags icon"/>
+              <!-- eslint-disable-next-line vue/no-v-html -->
+              <span v-html="textCreateTag.replace('%s', searchTerm)"/>
+            </div>
+            <div v-show="!shouldCreateTag">
+              <svg-icon name="octicon-git-branch"/>
+              <!-- eslint-disable-next-line vue/no-v-html -->
+              <span v-html="textCreateBranch.replace('%s', searchTerm)"/>
+            </div>
+            <div class="text small">
+              <span v-if="isViewBranch || release">{{ textCreateBranchFrom.replace('%s', branchName) }}</span>
+              <span v-else-if="isViewTag">{{ textCreateBranchFrom.replace('%s', tagName) }}</span>
+              <span v-else>{{ textCreateBranchFrom.replace('%s', commitIdShort) }}</span>
+            </div>
+          </a>
+          <form ref="newBranchForm" :action="formActionUrl" method="post">
+            <input type="hidden" name="_csrf" :value="csrfToken">
+            <input type="hidden" name="new_branch_name" v-model="searchTerm">
+            <input type="hidden" name="create_tag" v-model="shouldCreateTag">
+            <input type="hidden" name="current_path" v-model="treePath" v-if="treePath">
+          </form>
+        </div>
+      </div>
+      <div class="message" v-if="showNoResults && !isLoading">
+        {{ noResults }}
+      </div>
+    </div>
+  </div>
+</template>
 <style scoped>
 .branch-tag-tab {
   padding: 0 10px;
diff --git a/web_src/js/components/ScopedAccessTokenSelector.vue b/web_src/js/components/ScopedAccessTokenSelector.vue
index b4b9b979e..f6af7e447 100644
--- a/web_src/js/components/ScopedAccessTokenSelector.vue
+++ b/web_src/js/components/ScopedAccessTokenSelector.vue
@@ -1,28 +1,3 @@
-<template>
-  <div v-for="category in categories" :key="category" class="field gt-pl-2 gt-pb-2 access-token-category">
-    <label class="category-label" :for="'access-token-scope-' + category">
-      {{ category }}
-    </label>
-    <div class="gitea-select">
-      <select
-        class="ui selection access-token-select"
-        name="scope"
-        :id="'access-token-scope-' + category"
-      >
-        <option value="">
-          {{ noAccessLabel }}
-        </option>
-        <option :value="'read:' + category">
-          {{ readLabel }}
-        </option>
-        <option :value="'write:' + category">
-          {{ writeLabel }}
-        </option>
-      </select>
-    </div>
-  </div>
-</template>
-
 <script>
 import {createApp} from 'vue';
 import {hideElem, showElem} from '../utils/dom.js';
@@ -111,3 +86,27 @@ export function initScopedAccessTokenCategories() {
 }
 
 </script>
+<template>
+  <div v-for="category in categories" :key="category" class="field gt-pl-2 gt-pb-2 access-token-category">
+    <label class="category-label" :for="'access-token-scope-' + category">
+      {{ category }}
+    </label>
+    <div class="gitea-select">
+      <select
+        class="ui selection access-token-select"
+        name="scope"
+        :id="'access-token-scope-' + category"
+      >
+        <option value="">
+          {{ noAccessLabel }}
+        </option>
+        <option :value="'read:' + category">
+          {{ readLabel }}
+        </option>
+        <option :value="'write:' + category">
+          {{ writeLabel }}
+        </option>
+      </select>
+    </div>
+  </div>
+</template>