mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-06-02 21:49:25 +00:00
e2ab8a46ed
Some improvements to the Page Header and Tab UI. Original | New :--------:|:-------: ![image](https://user-images.githubusercontent.com/62170586/197360886-046f1016-ca39-4b69-8134-99ba88e3a0c2.png) | ![image](https://user-images.githubusercontent.com/62170586/197360819-7efd0d82-1412-465d-aefa-039164f97465.png) ![image](https://user-images.githubusercontent.com/62170586/197360872-f2ece5fd-7c0b-4e2c-8629-31524a412af5.png) | ![image](https://user-images.githubusercontent.com/62170586/197360830-49f09e0d-619e-4fa9-8e38-8d05d9404185.png) ![image](https://user-images.githubusercontent.com/62170586/197281776-e3de6441-9417-4614-8b25-1aaef0b8da61.png) | ![image](https://user-images.githubusercontent.com/62170586/197281698-40c66d34-76f3-4fd5-97e3-1c422b74844c.png) ![image](https://user-images.githubusercontent.com/62170586/196609248-ff150c6e-2995-4bcc-8573-49ffaf388446.png) | ![image](https://user-images.githubusercontent.com/62170586/197323734-7c1a1b79-0f41-4bf2-96a3-dd38df9e1415.png) ![image](https://user-images.githubusercontent.com/62170586/196609329-b7a6f37e-e8c2-4004-a98b-73f837122ff8.png) | ![image](https://user-images.githubusercontent.com/62170586/197323882-10141ffd-7411-4493-8291-b8000adc3cc5.png) What? - Create a new Scaffold component, which includes the header and tabs required for a page. - Use this component to wrap all the views that have a header. - Ensures consistency in headers between different pages. - [x] Add support to use custom html/component in place of title (for repo page, pipeline page, etc) - [x] Add support of right icon buttons (for repo page, pipeline page, etc) - [x] Refactor tabs handling using compositions (useTabsProvider, useTabsClient) - [x] Make new header ui resposive
157 lines
4.8 KiB
Vue
157 lines
4.8 KiB
Vue
<template>
|
|
<Scaffold
|
|
v-if="repo && repoPermissions && $route.meta.repoHeader"
|
|
v-model:activeTab="activeTab"
|
|
enable-tabs
|
|
disable-hash-mode
|
|
>
|
|
<template #title>
|
|
<span class="flex">
|
|
<router-link :to="{ name: 'repos-owner', params: { repoOwner } }" class="hover:underline">{{
|
|
repoOwner
|
|
}}</router-link>
|
|
{{ ` / ${repo.name}` }}
|
|
</span>
|
|
</template>
|
|
<template #titleActions>
|
|
<a v-if="badgeUrl" :href="badgeUrl" target="_blank" class="ml-2">
|
|
<img :src="badgeUrl" />
|
|
</a>
|
|
<a
|
|
:href="repo.link_url"
|
|
target="_blank"
|
|
class="flex p-1 rounded-full text-color hover:bg-gray-200 hover:text-gray-700 dark:hover:bg-gray-600"
|
|
>
|
|
<Icon v-if="forge === 'github'" name="github" />
|
|
<Icon v-else-if="forge === 'gitea'" name="gitea" />
|
|
<Icon v-else-if="forge === 'gitlab'" name="gitlab" />
|
|
<Icon v-else-if="forge === 'bitbucket' || forge === 'stash'" name="bitbucket" />
|
|
<Icon v-else name="repo" />
|
|
</a>
|
|
<IconButton
|
|
v-if="repoPermissions.admin"
|
|
:to="{ name: 'repo-settings' }"
|
|
:title="$t('repo.settings.settings')"
|
|
icon="settings"
|
|
/>
|
|
</template>
|
|
|
|
<template #tabActions>
|
|
<Button
|
|
v-if="repoPermissions.push"
|
|
:text="$t('repo.manual_pipeline.trigger')"
|
|
@click="showManualPipelinePopup = true"
|
|
/>
|
|
<ManualPipelinePopup :open="showManualPipelinePopup" @close="showManualPipelinePopup = false" />
|
|
</template>
|
|
|
|
<Tab id="activity" :title="$t('repo.activity')" />
|
|
<Tab id="branches" :title="$t('repo.branches')" />
|
|
|
|
<router-view />
|
|
</Scaffold>
|
|
<router-view v-else-if="repo && repoPermissions" />
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed, onMounted, provide, ref, toRef, watch } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
import Icon from '~/components/atomic/Icon.vue';
|
|
import IconButton from '~/components/atomic/IconButton.vue';
|
|
import ManualPipelinePopup from '~/components/layout/popups/ManualPipelinePopup.vue';
|
|
import Scaffold from '~/components/layout/scaffold/Scaffold.vue';
|
|
import Tab from '~/components/layout/scaffold/Tab.vue';
|
|
import useApiClient from '~/compositions/useApiClient';
|
|
import useAuthentication from '~/compositions/useAuthentication';
|
|
import useConfig from '~/compositions/useConfig';
|
|
import useNotifications from '~/compositions/useNotifications';
|
|
import { RepoPermissions } from '~/lib/api/types';
|
|
import PipelineStore from '~/store/pipelines';
|
|
import RepoStore from '~/store/repos';
|
|
|
|
const props = defineProps({
|
|
repoOwner: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
|
|
repoName: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
});
|
|
|
|
const repoOwner = toRef(props, 'repoOwner');
|
|
const repoName = toRef(props, 'repoName');
|
|
const repoStore = RepoStore();
|
|
const pipelineStore = PipelineStore();
|
|
const apiClient = useApiClient();
|
|
const notifications = useNotifications();
|
|
const { isAuthenticated } = useAuthentication();
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const i18n = useI18n();
|
|
|
|
const { forge } = useConfig();
|
|
const repo = repoStore.getRepo(repoOwner, repoName);
|
|
const repoPermissions = ref<RepoPermissions>();
|
|
const pipelines = pipelineStore.getSortedPipelines(repoOwner, repoName);
|
|
provide('repo', repo);
|
|
provide('repo-permissions', repoPermissions);
|
|
provide('pipelines', pipelines);
|
|
|
|
const showManualPipelinePopup = ref(false);
|
|
|
|
async function loadRepo() {
|
|
repoPermissions.value = await apiClient.getRepoPermissions(repoOwner.value, repoName.value);
|
|
if (!repoPermissions.value.pull) {
|
|
notifications.notify({ type: 'error', title: i18n.t('repo.not_allowed') });
|
|
// no access and not authenticated, redirect to login
|
|
if (!isAuthenticated) {
|
|
await router.replace({ name: 'login', query: { url: route.fullPath } });
|
|
return;
|
|
}
|
|
await router.replace({ name: 'home' });
|
|
return;
|
|
}
|
|
|
|
const apiRepo = await repoStore.loadRepo(repoOwner.value, repoName.value);
|
|
if (apiRepo.full_name !== `${repoOwner.value}/${repoName.value}`) {
|
|
await router.replace({
|
|
name: route.name ? route.name : 'repo',
|
|
params: { repoOwner: apiRepo.owner, repoName: apiRepo.name },
|
|
});
|
|
return;
|
|
}
|
|
await pipelineStore.loadPipelines(repoOwner.value, repoName.value);
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadRepo();
|
|
});
|
|
|
|
watch([repoOwner, repoName], () => {
|
|
loadRepo();
|
|
});
|
|
|
|
const badgeUrl = computed(() => `/api/badges/${repo.value.owner}/${repo.value.name}/status.svg`);
|
|
|
|
const activeTab = computed({
|
|
get() {
|
|
if (route.name === 'repo-branches' || route.name === 'repo-branch') {
|
|
return 'branches';
|
|
}
|
|
return 'activity';
|
|
},
|
|
set(tab: string) {
|
|
if (tab === 'branches') {
|
|
router.push({ name: 'repo-branches' });
|
|
} else {
|
|
router.push({ name: 'repo' });
|
|
}
|
|
},
|
|
});
|
|
</script>
|