mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 11:21:02 +00:00
Move manual popup to own page (#3981)
This commit is contained in:
parent
c864f24ae4
commit
2d1bc9f15c
6 changed files with 168 additions and 142 deletions
|
@ -360,7 +360,8 @@ func GetRepoBranches(c *gin.Context) {
|
|||
|
||||
branches, err := _forge.Branches(c, user, repo, session.Pagination(c))
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
log.Error().Err(err).Msg("failed to load branches")
|
||||
c.String(http.StatusInternalServerError, "failed to load branches: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
"desc": "Specify additional variables to use in your pipeline. Variables with the same name will be overwritten.",
|
||||
"name": "Variable name",
|
||||
"value": "Variable value"
|
||||
}
|
||||
},
|
||||
"show_pipelines": "Show pipelines"
|
||||
},
|
||||
"deploy_pipeline": {
|
||||
"title": "Trigger deployment event for current pipeline #{pipelineId}",
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
<template>
|
||||
<Popup :open="open" @close="$emit('close')">
|
||||
<Panel v-if="!loading">
|
||||
<form @submit.prevent="triggerManualPipeline">
|
||||
<span class="text-xl text-wp-text-100">{{ $t('repo.manual_pipeline.title') }}</span>
|
||||
<InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.select_branch')">
|
||||
<SelectField :id="id" v-model="payload.branch" :options="branches" required />
|
||||
</InputField>
|
||||
<InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.variables.title')">
|
||||
<span class="text-sm text-wp-text-alt-100 mb-2">{{ $t('repo.manual_pipeline.variables.desc') }}</span>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div v-for="(_, i) in payload.variables" :key="i" class="flex gap-4">
|
||||
<TextField
|
||||
:id="id"
|
||||
v-model="payload.variables[i].name"
|
||||
:placeholder="$t('repo.manual_pipeline.variables.name')"
|
||||
/>
|
||||
<TextField
|
||||
:id="id"
|
||||
v-model="payload.variables[i].value"
|
||||
:placeholder="$t('repo.manual_pipeline.variables.value')"
|
||||
/>
|
||||
<div class="w-10 flex-shrink-0">
|
||||
<Button
|
||||
v-if="i !== payload.variables.length - 1"
|
||||
color="red"
|
||||
class="ml-auto"
|
||||
:title="$t('repo.manual_pipeline.variables.delete')"
|
||||
@click="deleteVar(i)"
|
||||
>
|
||||
<Icon name="remove" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</InputField>
|
||||
<Button type="submit" :text="$t('repo.manual_pipeline.trigger')" />
|
||||
</form>
|
||||
</Panel>
|
||||
</Popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
import SelectField from '~/components/form/SelectField.vue';
|
||||
import TextField from '~/components/form/TextField.vue';
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import Popup from '~/components/layout/Popup.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { inject } from '~/compositions/useInjectProvide';
|
||||
import { usePaginate } from '~/compositions/usePaginate';
|
||||
|
||||
defineProps<{
|
||||
open: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'close'): void;
|
||||
}>();
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
||||
const repo = inject('repo');
|
||||
|
||||
const router = useRouter();
|
||||
const branches = ref<{ text: string; value: string }[]>([]);
|
||||
const payload = ref<{ branch: string; variables: { name: string; value: string }[] }>({
|
||||
branch: 'main',
|
||||
variables: [
|
||||
{
|
||||
name: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const pipelineOptions = computed(() => {
|
||||
const variables = Object.fromEntries(
|
||||
payload.value.variables.filter((e) => e.name !== '').map((item) => [item.name, item.value]),
|
||||
);
|
||||
return {
|
||||
...payload.value,
|
||||
variables,
|
||||
};
|
||||
});
|
||||
|
||||
const loading = ref(true);
|
||||
onMounted(async () => {
|
||||
const data = await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, { page }));
|
||||
branches.value = data.map((e) => ({
|
||||
text: e,
|
||||
value: e,
|
||||
}));
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
watch(
|
||||
payload,
|
||||
() => {
|
||||
if (payload.value.variables[payload.value.variables.length - 1].name !== '') {
|
||||
payload.value.variables.push({
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function deleteVar(index: number) {
|
||||
payload.value.variables.splice(index, 1);
|
||||
}
|
||||
|
||||
async function triggerManualPipeline() {
|
||||
loading.value = true;
|
||||
const pipeline = await apiClient.createPipeline(repo.value.id, pipelineOptions.value);
|
||||
|
||||
emit('close');
|
||||
|
||||
await router.push({
|
||||
name: 'repo-pipeline',
|
||||
params: {
|
||||
pipelineId: pipeline.number,
|
||||
},
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
|
@ -103,6 +103,12 @@ const routes: RouteRecordRaw[] = [
|
|||
meta: { authentication: 'required' },
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: 'manual',
|
||||
name: 'repo-manual',
|
||||
component: (): Component => import('~/views/repo/RepoManualPipeline.vue'),
|
||||
meta: { authentication: 'required', repoHeader: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
149
web/src/views/repo/RepoManualPipeline.vue
Normal file
149
web/src/views/repo/RepoManualPipeline.vue
Normal file
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<Panel v-if="!loading">
|
||||
<form @submit.prevent="triggerManualPipeline">
|
||||
<span class="text-xl text-wp-text-100">{{ $t('repo.manual_pipeline.title') }}</span>
|
||||
<InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.select_branch')">
|
||||
<SelectField :id="id" v-model="payload.branch" :options="branches" required />
|
||||
</InputField>
|
||||
<InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.variables.title')">
|
||||
<span class="text-sm text-wp-text-alt-100 mb-2">{{ $t('repo.manual_pipeline.variables.desc') }}</span>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div v-for="(_, i) in payload.variables" :key="i" class="flex gap-4">
|
||||
<TextField
|
||||
:id="id"
|
||||
v-model="payload.variables[i].name"
|
||||
:placeholder="$t('repo.manual_pipeline.variables.name')"
|
||||
/>
|
||||
<TextField
|
||||
:id="id"
|
||||
v-model="payload.variables[i].value"
|
||||
:placeholder="$t('repo.manual_pipeline.variables.value')"
|
||||
/>
|
||||
<div class="w-10 flex-shrink-0">
|
||||
<Button
|
||||
v-if="i !== payload.variables.length - 1"
|
||||
color="red"
|
||||
class="ml-auto"
|
||||
:title="$t('repo.manual_pipeline.variables.delete')"
|
||||
@click="deleteVar(i)"
|
||||
>
|
||||
<Icon name="remove" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</InputField>
|
||||
<Button type="submit" :text="$t('repo.manual_pipeline.trigger')" />
|
||||
</form>
|
||||
</Panel>
|
||||
<div v-else class="flex justify-center text-wp-text-100">
|
||||
<Icon name="spinner" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useNotification } from '@kyvg/vue3-notification';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed, onMounted, ref, inject as vueInject, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
import SelectField from '~/components/form/SelectField.vue';
|
||||
import TextField from '~/components/form/TextField.vue';
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { inject } from '~/compositions/useInjectProvide';
|
||||
import { usePaginate } from '~/compositions/usePaginate';
|
||||
import type { RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
defineProps<{
|
||||
open: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'close'): void;
|
||||
}>();
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotification();
|
||||
const i18n = useI18n();
|
||||
|
||||
const repo = inject('repo');
|
||||
const repoPermissions = vueInject<Ref<RepoPermissions>>('repo-permissions');
|
||||
if (!repoPermissions) {
|
||||
throw new Error('Unexpected: "repo" and "repoPermissions" should be provided at this place');
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
const branches = ref<{ text: string; value: string }[]>([]);
|
||||
const payload = ref<{ branch: string; variables: { name: string; value: string }[] }>({
|
||||
branch: 'main',
|
||||
variables: [
|
||||
{
|
||||
name: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const pipelineOptions = computed(() => {
|
||||
const variables = Object.fromEntries(
|
||||
payload.value.variables.filter((e) => e.name !== '').map((item) => [item.name, item.value]),
|
||||
);
|
||||
return {
|
||||
...payload.value,
|
||||
variables,
|
||||
};
|
||||
});
|
||||
|
||||
const loading = ref(true);
|
||||
onMounted(async () => {
|
||||
if (!repoPermissions.value.push) {
|
||||
notifications.notify({ type: 'error', title: i18n.t('repo.settings.not_allowed') });
|
||||
await router.replace({ name: 'home' });
|
||||
}
|
||||
|
||||
const data = await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, { page }));
|
||||
branches.value = data.map((e) => ({
|
||||
text: e,
|
||||
value: e,
|
||||
}));
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
watch(
|
||||
payload,
|
||||
() => {
|
||||
if (payload.value.variables[payload.value.variables.length - 1].name !== '') {
|
||||
payload.value.variables.push({
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function deleteVar(index: number) {
|
||||
payload.value.variables.splice(index, 1);
|
||||
}
|
||||
|
||||
async function triggerManualPipeline() {
|
||||
loading.value = true;
|
||||
const pipeline = await apiClient.createPipeline(repo.value.id, pipelineOptions.value);
|
||||
|
||||
emit('close');
|
||||
|
||||
await router.push({
|
||||
name: 'repo-pipeline',
|
||||
params: {
|
||||
pipelineId: pipeline.number,
|
||||
},
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
|
@ -30,11 +30,17 @@
|
|||
|
||||
<template #tabActions>
|
||||
<Button
|
||||
v-if="repoPermissions.push"
|
||||
v-if="repoPermissions.push && route.name !== 'repo-manual'"
|
||||
:text="$t('repo.manual_pipeline.trigger')"
|
||||
@click="showManualPipelinePopup = true"
|
||||
start-icon="manual-pipeline"
|
||||
:to="{ name: 'repo-manual' }"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="repoPermissions.push"
|
||||
:text="$t('repo.manual_pipeline.show_pipelines')"
|
||||
start-icon="back"
|
||||
:to="{ name: 'repo' }"
|
||||
/>
|
||||
<ManualPipelinePopup :open="showManualPipelinePopup" @close="showManualPipelinePopup = false" />
|
||||
</template>
|
||||
|
||||
<Tab id="activity" :title="$t('repo.activity')" />
|
||||
|
@ -54,7 +60,6 @@ import { useRoute, useRouter } from 'vue-router';
|
|||
import Button from '~/components/atomic/Button.vue';
|
||||
import type { IconNames } 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';
|
||||
|
@ -97,8 +102,6 @@ const forgeIcon = computed<IconNames>(() => {
|
|||
return 'repo';
|
||||
});
|
||||
|
||||
const showManualPipelinePopup = ref(false);
|
||||
|
||||
async function loadRepo() {
|
||||
repoPermissions.value = await apiClient.getRepoPermissions(repositoryId.value);
|
||||
if (!repoPermissions.value.pull) {
|
||||
|
|
Loading…
Reference in a new issue