mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 03:41:01 +00:00
Add page to view all projects of a user / group (#741)
* ReposOwner view shows all repos owned by a person or organization uses /:repoOwner urls (e. g. ci.example.org/example-org) also includes a link from the repo page to the owner page related to #468 * ReposOwner remove add btn; msg if no projects; grid implements the changes suggested by @anbraten: - removed the `add repository` button - now shows a message `This organization / user does not have any projects yet.` when appropriate now uses `grid` instead of `flex` on desktop to keep the search bar centered and always in the same place
This commit is contained in:
parent
dbd048c5e9
commit
6bb964921e
4 changed files with 75 additions and 3 deletions
|
@ -21,6 +21,13 @@ const routes: RouteRecordRaw[] = [
|
||||||
component: (): Component => import('~/views/RepoAdd.vue'),
|
component: (): Component => import('~/views/RepoAdd.vue'),
|
||||||
meta: { authentication: 'required' },
|
meta: { authentication: 'required' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/:repoOwner',
|
||||||
|
name: 'repos-owner',
|
||||||
|
component: (): Component => import('~/views/ReposOwner.vue'),
|
||||||
|
props: true,
|
||||||
|
meta: { authentication: 'required' },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/:repoOwner/:repoName',
|
path: '/:repoOwner/:repoName',
|
||||||
name: 'repo-wrapper',
|
name: 'repo-wrapper',
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<FluidContainer class="flex flex-col">
|
<FluidContainer class="flex flex-col">
|
||||||
<div class="flex flex-row flex-wrap border-b pb-4 mb-4 items-center dark:border-dark-200">
|
<div class="flex flex-row flex-wrap md:grid md:grid-cols-3 border-b pb-4 mb-4 dark:border-dark-200">
|
||||||
<h1 class="text-xl text-gray-500">Repositories</h1>
|
<h1 class="text-xl text-gray-500">Repositories</h1>
|
||||||
<TextField v-model="search" class="w-auto md:ml-auto" placeholder="Search ..." />
|
<TextField v-model="search" class="w-auto md:ml-auto md:mr-auto" placeholder="Search ..." />
|
||||||
<Button class="md:ml-auto" :to="{ name: 'repo-add' }" start-icon="plus" text="Add repository" />
|
<Button class="md:ml-auto" :to="{ name: 'repo-add' }" start-icon="plus" text="Add repository" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
64
web/src/views/ReposOwner.vue
Normal file
64
web/src/views/ReposOwner.vue
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<FluidContainer class="flex flex-col">
|
||||||
|
<div class="flex flex-row flex-wrap md:grid md:grid-cols-3 border-b pb-4 mb-4 dark:border-dark-200">
|
||||||
|
<h1 class="text-xl text-gray-500">{{ repoOwner }}</h1>
|
||||||
|
<TextField v-model="search" class="w-auto md:ml-auto md:mr-auto" placeholder="Search ..." />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<ListItem
|
||||||
|
v-for="repo in searchedRepos"
|
||||||
|
:key="repo.id"
|
||||||
|
clickable
|
||||||
|
@click="$router.push({ name: 'repo', params: { repoName: repo.name, repoOwner: repo.owner } })"
|
||||||
|
>
|
||||||
|
<span class="text-gray-500">{{ `${repo.name}` }}</span>
|
||||||
|
</ListItem>
|
||||||
|
</div>
|
||||||
|
<div v-if="(searchedRepos || []).length <= 0" class="text-center">
|
||||||
|
<span class="text-gray-500 m-auto">This organization / user does not have any projects yet.</span>
|
||||||
|
</div>
|
||||||
|
</FluidContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
|
import TextField from '~/components/form/TextField.vue';
|
||||||
|
import FluidContainer from '~/components/layout/FluidContainer.vue';
|
||||||
|
import { useRepoSearch } from '~/compositions/useRepoSearch';
|
||||||
|
import RepoStore from '~/store/repos';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ReposOwner',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
FluidContainer,
|
||||||
|
ListItem,
|
||||||
|
TextField,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
repoOwner: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const repoStore = RepoStore();
|
||||||
|
// TODO: filter server side
|
||||||
|
const repos = computed(() => Object.values(repoStore.repos).filter((v) => v.owner === props.repoOwner));
|
||||||
|
const search = ref('');
|
||||||
|
|
||||||
|
const { searchedRepos } = useRepoSearch(repos, search);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await repoStore.loadRepos();
|
||||||
|
});
|
||||||
|
|
||||||
|
return { searchedRepos, search };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -2,7 +2,8 @@
|
||||||
<FluidContainer v-if="repo && repoPermissions && $route.meta.repoHeader">
|
<FluidContainer v-if="repo && repoPermissions && $route.meta.repoHeader">
|
||||||
<div class="flex flex-wrap border-b items-center pb-4 mb-4 dark:border-gray-600 justify-center">
|
<div class="flex flex-wrap border-b items-center pb-4 mb-4 dark:border-gray-600 justify-center">
|
||||||
<h1 class="text-xl text-gray-500 w-full md:w-auto text-center mb-4 md:mb-0">
|
<h1 class="text-xl text-gray-500 w-full md:w-auto text-center mb-4 md:mb-0">
|
||||||
{{ `${repo.owner} / ${repo.name}` }}
|
<router-link :to="{ name: 'repos-owner', params: { repoOwner } }">{{ repoOwner }}</router-link>
|
||||||
|
{{ ` / ${repo.name}` }}
|
||||||
</h1>
|
</h1>
|
||||||
<a v-if="badgeUrl" :href="badgeUrl" target="_blank" class="md:ml-auto">
|
<a v-if="badgeUrl" :href="badgeUrl" target="_blank" class="md:ml-auto">
|
||||||
<img :src="badgeUrl" />
|
<img :src="badgeUrl" />
|
||||||
|
|
Loading…
Reference in a new issue