mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-03-27 23:02:54 +00:00
Use Vue setup directive (#2165)
This commit is contained in:
parent
9cae5709f9
commit
3bdeb47d8c
26 changed files with 582 additions and 981 deletions
|
@ -45,9 +45,9 @@
|
|||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -69,74 +69,48 @@ const emptySecret = {
|
|||
event: [WebhookEvents.Push],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AdminSecretsTab',
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
|
||||
components: {
|
||||
Button,
|
||||
Panel,
|
||||
DocsLink,
|
||||
SecretList,
|
||||
SecretEdit,
|
||||
Warning,
|
||||
},
|
||||
const selectedSecret = ref<Partial<Secret>>();
|
||||
const isEditingSecret = computed(() => !!selectedSecret.value?.id);
|
||||
|
||||
setup() {
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
async function loadSecrets(page: number): Promise<Secret[] | null> {
|
||||
return apiClient.getGlobalSecretList(page);
|
||||
}
|
||||
|
||||
const selectedSecret = ref<Partial<Secret>>();
|
||||
const isEditingSecret = computed(() => !!selectedSecret.value?.id);
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
|
||||
async function loadSecrets(page: number): Promise<Secret[] | null> {
|
||||
return apiClient.getGlobalSecretList(page);
|
||||
}
|
||||
const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!selectedSecret.value) {
|
||||
throw new Error("Unexpected: Can't get secret");
|
||||
}
|
||||
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
|
||||
const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!selectedSecret.value) {
|
||||
throw new Error("Unexpected: Can't get secret");
|
||||
}
|
||||
|
||||
if (isEditingSecret.value) {
|
||||
await apiClient.updateGlobalSecret(selectedSecret.value);
|
||||
} else {
|
||||
await apiClient.createGlobalSecret(selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'admin.settings.secrets.saved' : 'admin.settings.secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
resetPage();
|
||||
});
|
||||
|
||||
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
||||
await apiClient.deleteGlobalSecret(_secret.name);
|
||||
notifications.notify({ title: i18n.t('admin.settings.secrets.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
|
||||
function editSecret(secret: Secret) {
|
||||
selectedSecret.value = cloneDeep(secret);
|
||||
}
|
||||
|
||||
function showAddSecret() {
|
||||
selectedSecret.value = cloneDeep(emptySecret);
|
||||
}
|
||||
|
||||
return {
|
||||
selectedSecret,
|
||||
secrets,
|
||||
isDeleting,
|
||||
isSaving,
|
||||
showAddSecret,
|
||||
createSecret,
|
||||
editSecret,
|
||||
deleteSecret,
|
||||
};
|
||||
},
|
||||
if (isEditingSecret.value) {
|
||||
await apiClient.updateGlobalSecret(selectedSecret.value);
|
||||
} else {
|
||||
await apiClient.createGlobalSecret(selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'admin.settings.secrets.saved' : 'admin.settings.secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
resetPage();
|
||||
});
|
||||
|
||||
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
||||
await apiClient.deleteGlobalSecret(_secret.name);
|
||||
notifications.notify({ title: i18n.t('admin.settings.secrets.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
|
||||
function editSecret(secret: Secret) {
|
||||
selectedSecret.value = cloneDeep(secret);
|
||||
}
|
||||
|
||||
function showAddSecret() {
|
||||
selectedSecret.value = cloneDeep(emptySecret);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -35,88 +35,59 @@
|
|||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, useAttrs } from 'vue';
|
||||
import { RouteLocationRaw, useRouter } from 'vue-router';
|
||||
|
||||
import Icon, { IconNames } from '~/components/atomic/Icon.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Button',
|
||||
|
||||
components: { Icon },
|
||||
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
title: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
|
||||
to: {
|
||||
type: [String, Object, null] as PropType<RouteLocationRaw | null>,
|
||||
default: null,
|
||||
},
|
||||
|
||||
color: {
|
||||
type: String as PropType<'blue' | 'green' | 'red' | 'gray'>,
|
||||
default: 'gray',
|
||||
},
|
||||
|
||||
startIcon: {
|
||||
type: String as PropType<IconNames | null>,
|
||||
default: null,
|
||||
},
|
||||
|
||||
endIcon: {
|
||||
type: String as PropType<IconNames | null>,
|
||||
default: null,
|
||||
},
|
||||
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
},
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
text: string;
|
||||
title?: string;
|
||||
disabled?: boolean;
|
||||
to: RouteLocationRaw | null;
|
||||
color: 'blue' | 'green' | 'red' | 'gray';
|
||||
startIcon: IconNames | null;
|
||||
endIcon: IconNames | null;
|
||||
isLoading?: boolean;
|
||||
}>(),
|
||||
{
|
||||
text: '',
|
||||
title: undefined,
|
||||
to: null,
|
||||
color: 'gray',
|
||||
startIcon: null,
|
||||
endIcon: null,
|
||||
},
|
||||
);
|
||||
|
||||
setup(props, { attrs }) {
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
async function doClick() {
|
||||
if (props.isLoading) {
|
||||
return;
|
||||
}
|
||||
async function doClick() {
|
||||
if (props.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!props.to) {
|
||||
return;
|
||||
}
|
||||
if (!props.to) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof props.to === 'string' && props.to.startsWith('http')) {
|
||||
window.location.href = props.to;
|
||||
return;
|
||||
}
|
||||
if (typeof props.to === 'string' && props.to.startsWith('http')) {
|
||||
window.location.href = props.to;
|
||||
return;
|
||||
}
|
||||
|
||||
await router.push(props.to);
|
||||
}
|
||||
await router.push(props.to);
|
||||
}
|
||||
|
||||
const passedClasses = computed(() => {
|
||||
const classes: Record<string, boolean> = {};
|
||||
const origClass = (attrs.class as string) || '';
|
||||
origClass.split(' ').forEach((c) => {
|
||||
classes[c] = true;
|
||||
});
|
||||
return classes;
|
||||
});
|
||||
|
||||
return { doClick, passedClasses };
|
||||
},
|
||||
const attrs = useAttrs();
|
||||
const passedClasses = computed(() => {
|
||||
const classes: Record<string, boolean> = {};
|
||||
const origClass = (attrs.class as string) || '';
|
||||
origClass.split(' ').forEach((c) => {
|
||||
classes[c] = true;
|
||||
});
|
||||
return classes;
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<router-link v-if="to" :to="to" :title="title" :aria-label="title" class="icon-button">
|
||||
<slot>
|
||||
<Icon :name="icon" />
|
||||
<Icon v-if="icon" :name="icon" />
|
||||
</slot>
|
||||
</router-link>
|
||||
<a
|
||||
|
@ -14,12 +14,12 @@
|
|||
rel="noopener noreferrer"
|
||||
>
|
||||
<slot>
|
||||
<Icon :name="icon" />
|
||||
<Icon v-if="icon" :name="icon" />
|
||||
</slot>
|
||||
</a>
|
||||
<button v-else :disabled="disabled" class="icon-button" type="button" :title="title" :aria-label="title">
|
||||
<slot>
|
||||
<Icon :name="icon" />
|
||||
<Icon v-if="icon" :name="icon" />
|
||||
</slot>
|
||||
<div v-if="isLoading" class="absolute left-0 top-0 right-0 bottom-0 flex items-center justify-center">
|
||||
<Icon name="loading" class="animate-spin" />
|
||||
|
@ -27,48 +27,27 @@
|
|||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
import Icon, { IconNames } from '~/components/atomic/Icon.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IconButton',
|
||||
|
||||
components: { Icon },
|
||||
|
||||
props: {
|
||||
icon: {
|
||||
type: String as PropType<IconNames>,
|
||||
default: '',
|
||||
},
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
|
||||
to: {
|
||||
type: [String, Object, null] as PropType<RouteLocationRaw | null>,
|
||||
default: null,
|
||||
},
|
||||
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
href: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
icon: IconNames | null;
|
||||
disabled?: boolean;
|
||||
to: RouteLocationRaw | null;
|
||||
isLoading?: boolean;
|
||||
title: string;
|
||||
href?: string;
|
||||
}>(),
|
||||
{
|
||||
icon: null,
|
||||
to: null,
|
||||
title: undefined,
|
||||
href: '',
|
||||
},
|
||||
});
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -6,17 +6,8 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Warning',
|
||||
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
text: string;
|
||||
}>();
|
||||
</script>
|
||||
|
|
|
@ -8,57 +8,34 @@
|
|||
@click="innerValue = !innerValue"
|
||||
/>
|
||||
<div class="flex flex-col ml-4">
|
||||
<label v-if="label" class="cursor-pointer text-wp-text-100" :for="`checkbox-${id}`">{{ label }}</label>
|
||||
<label class="cursor-pointer text-wp-text-100" :for="`checkbox-${id}`">{{ label }}</label>
|
||||
<span v-if="description" class="text-sm text-wp-text-alt-100">{{ description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Checkbox',
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
label: string;
|
||||
description?: string;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: boolean): void;
|
||||
}>();
|
||||
|
||||
label: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
description: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
emits: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'update:modelValue': (_value: boolean): boolean => true,
|
||||
},
|
||||
|
||||
setup: (props, ctx) => {
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
ctx.emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
const id = (Math.random() + 1).toString(36).substring(7);
|
||||
|
||||
return {
|
||||
id,
|
||||
innerValue,
|
||||
};
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
const id = (Math.random() + 1).toString(36).substring(7);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -10,55 +10,40 @@
|
|||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import Checkbox from './Checkbox.vue';
|
||||
import { CheckboxOption } from './form.types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CheckboxesField',
|
||||
|
||||
components: { Checkbox },
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array as PropType<CheckboxOption['value'][]>,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
options: {
|
||||
type: Array as PropType<CheckboxOption[]>,
|
||||
required: true,
|
||||
},
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: CheckboxOption['value'][];
|
||||
options: CheckboxOption[];
|
||||
}>(),
|
||||
{
|
||||
modelValue: () => [],
|
||||
options: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
emits: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'update:modelValue': (_value: CheckboxOption['value'][]): boolean => true,
|
||||
},
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: CheckboxOption['value'][]): void;
|
||||
}>();
|
||||
|
||||
setup: (props, ctx) => {
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
ctx.emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
function clickOption(option: CheckboxOption) {
|
||||
if (innerValue.value.includes(option.value)) {
|
||||
innerValue.value = innerValue.value.filter((o) => o !== option.value);
|
||||
} else {
|
||||
innerValue.value.push(option.value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
innerValue,
|
||||
clickOption,
|
||||
};
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
function clickOption(option: CheckboxOption) {
|
||||
if (innerValue.value.includes(option.value)) {
|
||||
innerValue.value = innerValue.value.filter((o) => o !== option.value);
|
||||
} else {
|
||||
innerValue.value.push(option.value);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2,45 +2,25 @@
|
|||
<TextField v-model="innerValue" :placeholder="placeholder" type="number" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import TextField from '~/components/form/TextField.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NumberField',
|
||||
const props = defineProps<{
|
||||
modelValue: number;
|
||||
placeholder?: string;
|
||||
}>();
|
||||
|
||||
components: { TextField },
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: number): void;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
emits: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'update:modelValue': (_value: number): boolean => true,
|
||||
},
|
||||
|
||||
setup: (props, ctx) => {
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value.toString(),
|
||||
set: (value) => {
|
||||
ctx.emit('update:modelValue', parseFloat(value));
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
innerValue,
|
||||
};
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value.toString(),
|
||||
set: (value) => {
|
||||
emit('update:modelValue', parseFloat(value));
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -15,50 +15,29 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import { RadioOption } from './form.types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RadioField',
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
options: RadioOption[];
|
||||
}>();
|
||||
|
||||
components: {},
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
options: {
|
||||
type: Array as PropType<RadioOption[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
emits: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'update:modelValue': (_value: RadioOption['value']): boolean => true,
|
||||
},
|
||||
|
||||
setup: (props, ctx) => {
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
ctx.emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
const id = (Math.random() + 1).toString(36).substring(7);
|
||||
|
||||
return {
|
||||
id,
|
||||
innerValue,
|
||||
};
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
const id = (Math.random() + 1).toString(36).substring(7);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -10,48 +10,32 @@
|
|||
</select>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import { SelectOption } from './form.types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SelectField',
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
options: {
|
||||
type: Array as PropType<SelectOption[]>,
|
||||
required: true,
|
||||
},
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string;
|
||||
placeholder: string;
|
||||
options: SelectOption[];
|
||||
}>(),
|
||||
{
|
||||
placeholder: '',
|
||||
options: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
emits: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'update:modelValue': (_value: SelectOption['value'] | null): boolean => true,
|
||||
},
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
setup: (props, ctx) => {
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (selectedValue) => {
|
||||
ctx.emit('update:modelValue', selectedValue);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
innerValue,
|
||||
};
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (selectedValue) => {
|
||||
emit('update:modelValue', selectedValue);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -19,55 +19,34 @@
|
|||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TextField',
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
|
||||
lines: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
},
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string;
|
||||
placeholder: string;
|
||||
type: string;
|
||||
lines: number;
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{
|
||||
modelValue: '',
|
||||
placeholder: '',
|
||||
type: 'text',
|
||||
lines: 1,
|
||||
},
|
||||
);
|
||||
|
||||
emits: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
'update:modelValue': (_value: string): boolean => true,
|
||||
},
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
setup: (props, ctx) => {
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
ctx.emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
innerValue,
|
||||
};
|
||||
const modelValue = toRef(props, 'modelValue');
|
||||
const innerValue = computed({
|
||||
get: () => modelValue.value,
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -37,9 +37,8 @@
|
|||
class="navbar-icon"
|
||||
:title="$t('admin.settings.settings')"
|
||||
:to="{ name: 'admin-settings' }"
|
||||
>
|
||||
<i-clarity-settings-solid />
|
||||
</IconButton>
|
||||
icon="settings"
|
||||
/>
|
||||
|
||||
<!-- Active Pipelines Indicator -->
|
||||
<ActivePipelines v-if="user" class="navbar-icon" />
|
||||
|
@ -53,8 +52,7 @@
|
|||
</nav>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import WoodpeckerLogo from '~/assets/logo.svg?component';
|
||||
|
@ -66,36 +64,20 @@ import { useDarkMode } from '~/compositions/useDarkMode';
|
|||
|
||||
import ActivePipelines from './ActivePipelines.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Navbar',
|
||||
const config = useConfig();
|
||||
const route = useRoute();
|
||||
const authentication = useAuthentication();
|
||||
const { user } = authentication;
|
||||
const { darkMode } = useDarkMode();
|
||||
const docsUrl = config.docs || undefined;
|
||||
const apiUrl = `${config.rootPath ?? ''}/swagger/index.html`;
|
||||
|
||||
components: { Button, ActivePipelines, IconButton, WoodpeckerLogo },
|
||||
function doLogin() {
|
||||
authentication.authenticate(route.fullPath);
|
||||
}
|
||||
|
||||
setup() {
|
||||
const config = useConfig();
|
||||
const route = useRoute();
|
||||
const authentication = useAuthentication();
|
||||
const { darkMode } = useDarkMode();
|
||||
const docsUrl = config.docs || undefined;
|
||||
const apiUrl = `${config.rootPath ?? ''}/swagger/index.html`;
|
||||
|
||||
function doLogin() {
|
||||
authentication.authenticate(route.fullPath);
|
||||
}
|
||||
|
||||
const version = config.version?.startsWith('next') ? 'next' : config.version;
|
||||
|
||||
return {
|
||||
darkMode,
|
||||
user: authentication.user,
|
||||
doLogin,
|
||||
docsUrl,
|
||||
version,
|
||||
apiUrl,
|
||||
enableSwagger: config.enableSwagger,
|
||||
};
|
||||
},
|
||||
});
|
||||
const version = config.version?.startsWith('next') ? 'next' : config.version;
|
||||
const { enableSwagger } = config;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -68,9 +68,9 @@
|
|||
</ListItem>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { Tooltip } from 'floating-vue';
|
||||
import { defineComponent, PropType, toRef } from 'vue';
|
||||
import { toRef } from 'vue';
|
||||
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
import ListItem from '~/components/atomic/ListItem.vue';
|
||||
|
@ -80,23 +80,10 @@ import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vu
|
|||
import usePipeline from '~/compositions/usePipeline';
|
||||
import { Pipeline } from '~/lib/api/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PipelineItem',
|
||||
const props = defineProps<{
|
||||
pipeline: Pipeline;
|
||||
}>();
|
||||
|
||||
components: { Icon, PipelineStatusIcon, ListItem, PipelineRunningIcon, Tooltip },
|
||||
|
||||
props: {
|
||||
pipeline: {
|
||||
type: Object as PropType<Pipeline>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const pipeline = toRef(props, 'pipeline');
|
||||
const { since, duration, message, prettyRef, created } = usePipeline(pipeline);
|
||||
|
||||
return { since, duration, message, prettyRef, pipelineStatusColors, created };
|
||||
},
|
||||
});
|
||||
const pipeline = toRef(props, 'pipeline');
|
||||
const { since, duration, message, prettyRef, created } = usePipeline(pipeline);
|
||||
</script>
|
||||
|
|
|
@ -2,60 +2,45 @@
|
|||
<span v-if="started" class="ml-auto text-sm">{{ duration }}</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||
import { PipelineStep, PipelineWorkflow } from '~/lib/api/types';
|
||||
import { durationAsNumber } from '~/utils/duration';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PipelineStepDuration',
|
||||
const props = defineProps<{
|
||||
step?: PipelineStep;
|
||||
workflow?: PipelineWorkflow;
|
||||
}>();
|
||||
|
||||
props: {
|
||||
step: {
|
||||
type: Object as PropType<PipelineStep>,
|
||||
default: undefined,
|
||||
},
|
||||
const step = toRef(props, 'step');
|
||||
const workflow = toRef(props, 'workflow');
|
||||
|
||||
workflow: {
|
||||
type: Object as PropType<PipelineWorkflow>,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
const durationRaw = computed(() => {
|
||||
const start = (step.value ? step.value?.start_time : workflow.value?.start_time) || 0;
|
||||
const end = (step.value ? step.value?.end_time : workflow.value?.end_time) || 0;
|
||||
|
||||
setup(props) {
|
||||
const step = toRef(props, 'step');
|
||||
const workflow = toRef(props, 'workflow');
|
||||
if (end === 0 && start === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const durationRaw = computed(() => {
|
||||
const start = (step.value ? step.value?.start_time : workflow.value?.start_time) || 0;
|
||||
const end = (step.value ? step.value?.end_time : workflow.value?.end_time) || 0;
|
||||
if (end === 0) {
|
||||
return Date.now() - start * 1000;
|
||||
}
|
||||
|
||||
if (end === 0 && start === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (end === 0) {
|
||||
return Date.now() - start * 1000;
|
||||
}
|
||||
|
||||
return (end - start) * 1000;
|
||||
});
|
||||
|
||||
const running = computed(() => (step.value ? step.value?.state : workflow.value?.state) === 'running');
|
||||
const { time: durationElapsed } = useElapsedTime(running, durationRaw);
|
||||
|
||||
const duration = computed(() => {
|
||||
if (durationElapsed.value === undefined) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return durationAsNumber(durationElapsed.value || 0);
|
||||
});
|
||||
const started = computed(() => (step.value ? step.value?.start_time : workflow.value?.start_time) !== undefined);
|
||||
|
||||
return { started, duration };
|
||||
},
|
||||
return (end - start) * 1000;
|
||||
});
|
||||
|
||||
const running = computed(() => (step.value ? step.value?.state : workflow.value?.state) === 'running');
|
||||
const { time: durationElapsed } = useElapsedTime(running, durationRaw);
|
||||
|
||||
const duration = computed(() => {
|
||||
if (durationElapsed.value === undefined) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return durationAsNumber(durationElapsed.value || 0);
|
||||
});
|
||||
const started = computed(() => (step.value ? step.value?.start_time : workflow.value?.start_time) !== undefined);
|
||||
</script>
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { computed, defineComponent, inject, onMounted, Ref, ref, watch } from 'vue';
|
||||
import { computed, inject, onMounted, Ref, ref, watch } from 'vue';
|
||||
|
||||
import { SelectOption } from '~/components/form/form.types';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
|
@ -52,81 +52,71 @@ import useConfig from '~/compositions/useConfig';
|
|||
import { usePaginate } from '~/compositions/usePaginate';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BadgeTab',
|
||||
const apiClient = useApiClient();
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
|
||||
components: { Panel, InputField, SelectField },
|
||||
const badgeType = useStorage('last-badge-type', 'markdown');
|
||||
|
||||
setup() {
|
||||
const apiClient = useApiClient();
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
|
||||
const badgeType = useStorage('last-badge-type', 'markdown');
|
||||
const defaultBranch = computed(() => repo.value.default_branch);
|
||||
const branches = ref<SelectOption[]>([]);
|
||||
const branch = ref<string>('');
|
||||
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
async function loadBranches() {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
|
||||
const defaultBranch = computed(() => repo.value.default_branch);
|
||||
const branches = ref<SelectOption[]>([]);
|
||||
const branch = ref<string>('');
|
||||
branches.value = (await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, page)))
|
||||
.map((b) => ({
|
||||
value: b,
|
||||
text: b,
|
||||
}))
|
||||
.filter((b) => b.value !== defaultBranch.value);
|
||||
branches.value.unshift({
|
||||
value: '',
|
||||
text: defaultBranch.value,
|
||||
});
|
||||
}
|
||||
|
||||
async function loadBranches() {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
const baseUrl = `${window.location.protocol}//${window.location.hostname}${
|
||||
window.location.port ? `:${window.location.port}` : ''
|
||||
}${useConfig().rootPath}`;
|
||||
const badgeUrl = computed(
|
||||
() => `/api/badges/${repo.value.id}/status.svg${branch.value !== '' ? `?branch=${branch.value}` : ''}`,
|
||||
);
|
||||
const repoUrl = computed(
|
||||
() => `/repos/${repo.value.id}${branch.value !== '' ? `/branches/${encodeURIComponent(branch.value)}` : ''}`,
|
||||
);
|
||||
|
||||
branches.value = (await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, page)))
|
||||
.map((b) => ({
|
||||
value: b,
|
||||
text: b,
|
||||
}))
|
||||
.filter((b) => b.value !== defaultBranch.value);
|
||||
branches.value.unshift({
|
||||
value: '',
|
||||
text: defaultBranch.value,
|
||||
});
|
||||
}
|
||||
const badgeContent = computed(() => {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
|
||||
const baseUrl = `${window.location.protocol}//${window.location.hostname}${
|
||||
window.location.port ? `:${window.location.port}` : ''
|
||||
}${useConfig().rootPath}`;
|
||||
const badgeUrl = computed(
|
||||
() => `/api/badges/${repo.value.id}/status.svg${branch.value !== '' ? `?branch=${branch.value}` : ''}`,
|
||||
);
|
||||
const repoUrl = computed(
|
||||
() => `/repos/${repo.value.id}${branch.value !== '' ? `/branches/${encodeURIComponent(branch.value)}` : ''}`,
|
||||
);
|
||||
if (badgeType.value === 'url') {
|
||||
return `${baseUrl}${badgeUrl.value}`;
|
||||
}
|
||||
|
||||
const badgeContent = computed(() => {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
if (badgeType.value === 'markdown') {
|
||||
return `[](${baseUrl}${repoUrl.value})`;
|
||||
}
|
||||
|
||||
if (badgeType.value === 'url') {
|
||||
return `${baseUrl}${badgeUrl.value}`;
|
||||
}
|
||||
if (badgeType.value === 'html') {
|
||||
return `<a href="${baseUrl}${repoUrl.value}" target="_blank">\n <img src="${baseUrl}${badgeUrl.value}" alt="status-badge" />\n</a>`;
|
||||
}
|
||||
|
||||
if (badgeType.value === 'markdown') {
|
||||
return `[](${baseUrl}${repoUrl.value})`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
if (badgeType.value === 'html') {
|
||||
return `<a href="${baseUrl}${repoUrl.value}" target="_blank">\n <img src="${baseUrl}${badgeUrl.value}" alt="status-badge" />\n</a>`;
|
||||
}
|
||||
onMounted(() => {
|
||||
loadBranches();
|
||||
});
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadBranches();
|
||||
});
|
||||
|
||||
watch(repo, () => {
|
||||
loadBranches();
|
||||
});
|
||||
|
||||
return { badgeType, branches, branch, badgeContent, badgeUrl };
|
||||
},
|
||||
watch(repo, () => {
|
||||
loadBranches();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -88,8 +88,8 @@
|
|||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, onMounted, Ref, ref } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted, Ref, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -108,101 +108,84 @@ import useNotifications from '~/compositions/useNotifications';
|
|||
import { Repo, RepoSettings, RepoVisibility, WebhookEvents } from '~/lib/api/types';
|
||||
import { useRepoStore } from '~/store/repos';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'GeneralTab',
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const { user } = useAuthentication();
|
||||
const repoStore = useRepoStore();
|
||||
const i18n = useI18n();
|
||||
|
||||
components: { Button, Panel, InputField, TextField, RadioField, NumberField, Checkbox, CheckboxesField },
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoSettings = ref<RepoSettings>();
|
||||
|
||||
setup() {
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const { user } = useAuthentication();
|
||||
const repoStore = useRepoStore();
|
||||
const i18n = useI18n();
|
||||
function loadRepoSettings() {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo should be set');
|
||||
}
|
||||
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoSettings = ref<RepoSettings>();
|
||||
repoSettings.value = {
|
||||
config_file: repo.value.config_file,
|
||||
timeout: repo.value.timeout,
|
||||
visibility: repo.value.visibility,
|
||||
gated: repo.value.gated,
|
||||
trusted: repo.value.trusted,
|
||||
allow_pr: repo.value.allow_pr,
|
||||
cancel_previous_pipeline_events: repo.value.cancel_previous_pipeline_events || [],
|
||||
netrc_only_trusted: repo.value.netrc_only_trusted,
|
||||
};
|
||||
}
|
||||
|
||||
function loadRepoSettings() {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo should be set');
|
||||
}
|
||||
async function loadRepo() {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo should be set');
|
||||
}
|
||||
|
||||
repoSettings.value = {
|
||||
config_file: repo.value.config_file,
|
||||
timeout: repo.value.timeout,
|
||||
visibility: repo.value.visibility,
|
||||
gated: repo.value.gated,
|
||||
trusted: repo.value.trusted,
|
||||
allow_pr: repo.value.allow_pr,
|
||||
cancel_previous_pipeline_events: repo.value.cancel_previous_pipeline_events || [],
|
||||
netrc_only_trusted: repo.value.netrc_only_trusted,
|
||||
};
|
||||
}
|
||||
await repoStore.loadRepo(repo.value.id);
|
||||
loadRepoSettings();
|
||||
}
|
||||
|
||||
async function loadRepo() {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo should be set');
|
||||
}
|
||||
const { doSubmit: saveRepoSettings, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo should be set');
|
||||
}
|
||||
|
||||
await repoStore.loadRepo(repo.value.id);
|
||||
loadRepoSettings();
|
||||
}
|
||||
if (!repoSettings.value) {
|
||||
throw new Error('Unexpected: Repo-Settings should be set');
|
||||
}
|
||||
|
||||
const { doSubmit: saveRepoSettings, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!repo) {
|
||||
throw new Error('Unexpected: Repo should be set');
|
||||
}
|
||||
|
||||
if (!repoSettings.value) {
|
||||
throw new Error('Unexpected: Repo-Settings should be set');
|
||||
}
|
||||
|
||||
await apiClient.updateRepo(repo.value.id, repoSettings.value);
|
||||
await loadRepo();
|
||||
notifications.notify({ title: i18n.t('repo.settings.general.success'), type: 'success' });
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadRepoSettings();
|
||||
});
|
||||
|
||||
const projectVisibilityOptions: RadioOption[] = [
|
||||
{
|
||||
value: RepoVisibility.Public,
|
||||
text: i18n.t('repo.settings.general.visibility.public.public'),
|
||||
description: i18n.t('repo.settings.general.visibility.public.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Internal,
|
||||
text: i18n.t('repo.settings.general.visibility.internal.internal'),
|
||||
description: i18n.t('repo.settings.general.visibility.internal.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Private,
|
||||
text: i18n.t('repo.settings.general.visibility.private.private'),
|
||||
description: i18n.t('repo.settings.general.visibility.private.desc'),
|
||||
},
|
||||
];
|
||||
|
||||
const cancelPreviousPipelineEventsOptions: CheckboxOption[] = [
|
||||
{ value: WebhookEvents.Push, text: i18n.t('repo.pipeline.event.push') },
|
||||
{ value: WebhookEvents.Tag, text: i18n.t('repo.pipeline.event.tag') },
|
||||
{
|
||||
value: WebhookEvents.PullRequest,
|
||||
text: i18n.t('repo.pipeline.event.pr'),
|
||||
},
|
||||
{ value: WebhookEvents.Deploy, text: i18n.t('repo.pipeline.event.deploy') },
|
||||
];
|
||||
|
||||
return {
|
||||
user,
|
||||
repoSettings,
|
||||
isSaving,
|
||||
saveRepoSettings,
|
||||
projectVisibilityOptions,
|
||||
cancelPreviousPipelineEventsOptions,
|
||||
};
|
||||
},
|
||||
await apiClient.updateRepo(repo.value.id, repoSettings.value);
|
||||
await loadRepo();
|
||||
notifications.notify({ title: i18n.t('repo.settings.general.success'), type: 'success' });
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadRepoSettings();
|
||||
});
|
||||
|
||||
const projectVisibilityOptions: RadioOption[] = [
|
||||
{
|
||||
value: RepoVisibility.Public,
|
||||
text: i18n.t('repo.settings.general.visibility.public.public'),
|
||||
description: i18n.t('repo.settings.general.visibility.public.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Internal,
|
||||
text: i18n.t('repo.settings.general.visibility.internal.internal'),
|
||||
description: i18n.t('repo.settings.general.visibility.internal.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Private,
|
||||
text: i18n.t('repo.settings.general.visibility.private.private'),
|
||||
description: i18n.t('repo.settings.general.visibility.private.desc'),
|
||||
},
|
||||
];
|
||||
|
||||
const cancelPreviousPipelineEventsOptions: CheckboxOption[] = [
|
||||
{ value: WebhookEvents.Push, text: i18n.t('repo.pipeline.event.push') },
|
||||
{ value: WebhookEvents.Tag, text: i18n.t('repo.pipeline.event.tag') },
|
||||
{
|
||||
value: WebhookEvents.PullRequest,
|
||||
text: i18n.t('repo.pipeline.event.pr'),
|
||||
},
|
||||
{ value: WebhookEvents.Deploy, text: i18n.t('repo.pipeline.event.deploy') },
|
||||
];
|
||||
</script>
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, Ref, ref } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -101,74 +101,56 @@ import { usePagination } from '~/compositions/usePaginate';
|
|||
import { Repo } from '~/lib/api/types';
|
||||
import { Registry } from '~/lib/api/types/registry';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RegistriesTab',
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
|
||||
components: {
|
||||
Button,
|
||||
Panel,
|
||||
ListItem,
|
||||
IconButton,
|
||||
InputField,
|
||||
TextField,
|
||||
DocsLink,
|
||||
},
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const selectedRegistry = ref<Partial<Registry>>();
|
||||
const isEditingRegistry = computed(() => !!selectedRegistry.value?.id);
|
||||
|
||||
setup() {
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
async function loadRegistries(page: number): Promise<Registry[] | null> {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const selectedRegistry = ref<Partial<Registry>>();
|
||||
const isEditingRegistry = computed(() => !!selectedRegistry.value?.id);
|
||||
return apiClient.getRegistryList(repo.value.id, page);
|
||||
}
|
||||
|
||||
async function loadRegistries(page: number): Promise<Registry[] | null> {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
const { resetPage, data: registries } = usePagination(loadRegistries, () => !selectedRegistry.value);
|
||||
|
||||
return apiClient.getRegistryList(repo.value.id, page);
|
||||
}
|
||||
const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
const { resetPage, data: registries } = usePagination(loadRegistries, () => !selectedRegistry.value);
|
||||
if (!selectedRegistry.value) {
|
||||
throw new Error("Unexpected: Can't get registry");
|
||||
}
|
||||
|
||||
const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
if (isEditingRegistry.value) {
|
||||
await apiClient.updateRegistry(repo.value.id, selectedRegistry.value);
|
||||
} else {
|
||||
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(
|
||||
isEditingRegistry.value ? 'repo.settings.registries.saved' : i18n.t('repo.settings.registries.created'),
|
||||
),
|
||||
type: 'success',
|
||||
});
|
||||
selectedRegistry.value = undefined;
|
||||
resetPage();
|
||||
});
|
||||
|
||||
if (!selectedRegistry.value) {
|
||||
throw new Error("Unexpected: Can't get registry");
|
||||
}
|
||||
const { doSubmit: deleteRegistry, isLoading: isDeleting } = useAsyncAction(async (_registry: Registry) => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
if (isEditingRegistry.value) {
|
||||
await apiClient.updateRegistry(repo.value.id, selectedRegistry.value);
|
||||
} else {
|
||||
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(
|
||||
isEditingRegistry.value ? 'repo.settings.registries.saved' : i18n.t('repo.settings.registries.created'),
|
||||
),
|
||||
type: 'success',
|
||||
});
|
||||
selectedRegistry.value = undefined;
|
||||
resetPage();
|
||||
});
|
||||
|
||||
const { doSubmit: deleteRegistry, isLoading: isDeleting } = useAsyncAction(async (_registry: Registry) => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
const registryAddress = encodeURIComponent(_registry.address);
|
||||
await apiClient.deleteRegistry(repo.value.id, registryAddress);
|
||||
notifications.notify({ title: i18n.t('repo.settings.registries.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
|
||||
return { selectedRegistry, registries, isEditingRegistry, isSaving, isDeleting, createRegistry, deleteRegistry };
|
||||
},
|
||||
const registryAddress = encodeURIComponent(_registry.address);
|
||||
await apiClient.deleteRegistry(repo.value.id, registryAddress);
|
||||
notifications.notify({ title: i18n.t('repo.settings.registries.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -38,9 +38,9 @@
|
|||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { computed, defineComponent, inject, Ref, ref } from 'vue';
|
||||
import { computed, inject, Ref, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -61,86 +61,61 @@ const emptySecret = {
|
|||
event: [WebhookEvents.Push],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SecretsTab',
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
|
||||
components: {
|
||||
Button,
|
||||
Panel,
|
||||
DocsLink,
|
||||
SecretList,
|
||||
SecretEdit,
|
||||
},
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const selectedSecret = ref<Partial<Secret>>();
|
||||
const isEditingSecret = computed(() => !!selectedSecret.value?.id);
|
||||
|
||||
setup() {
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const i18n = useI18n();
|
||||
async function loadSecrets(page: number): Promise<Secret[] | null> {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const selectedSecret = ref<Partial<Secret>>();
|
||||
const isEditingSecret = computed(() => !!selectedSecret.value?.id);
|
||||
return apiClient.getSecretList(repo.value.id, page);
|
||||
}
|
||||
|
||||
async function loadSecrets(page: number): Promise<Secret[] | null> {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
|
||||
return apiClient.getSecretList(repo.value.id, page);
|
||||
}
|
||||
const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
if (!selectedSecret.value) {
|
||||
throw new Error("Unexpected: Can't get secret");
|
||||
}
|
||||
|
||||
const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async () => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
if (!selectedSecret.value) {
|
||||
throw new Error("Unexpected: Can't get secret");
|
||||
}
|
||||
|
||||
if (isEditingSecret.value) {
|
||||
await apiClient.updateSecret(repo.value.id, selectedSecret.value);
|
||||
} else {
|
||||
await apiClient.createSecret(repo.value.id, selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'repo.settings.secrets.saved' : 'repo.settings.secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
resetPage();
|
||||
});
|
||||
|
||||
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
await apiClient.deleteSecret(repo.value.id, _secret.name);
|
||||
notifications.notify({ title: i18n.t('repo.settings.secrets.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
|
||||
function editSecret(secret: Secret) {
|
||||
selectedSecret.value = cloneDeep(secret);
|
||||
}
|
||||
|
||||
function showAddSecret() {
|
||||
selectedSecret.value = cloneDeep(emptySecret);
|
||||
}
|
||||
|
||||
return {
|
||||
selectedSecret,
|
||||
secrets,
|
||||
isDeleting,
|
||||
isSaving,
|
||||
showAddSecret,
|
||||
createSecret,
|
||||
editSecret,
|
||||
deleteSecret,
|
||||
};
|
||||
},
|
||||
if (isEditingSecret.value) {
|
||||
await apiClient.updateSecret(repo.value.id, selectedSecret.value);
|
||||
} else {
|
||||
await apiClient.createSecret(repo.value.id, selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'repo.settings.secrets.saved' : 'repo.settings.secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
resetPage();
|
||||
});
|
||||
|
||||
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
await apiClient.deleteSecret(repo.value.id, _secret.name);
|
||||
notifications.notify({ title: i18n.t('repo.settings.secrets.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
|
||||
function editSecret(secret: Secret) {
|
||||
selectedSecret.value = cloneDeep(secret);
|
||||
}
|
||||
|
||||
function showAddSecret() {
|
||||
selectedSecret.value = cloneDeep(emptySecret);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -6,7 +6,7 @@ declare global {
|
|||
WOODPECKER_DOCS: string | undefined;
|
||||
WOODPECKER_VERSION: string | undefined;
|
||||
WOODPECKER_CSRF: string | undefined;
|
||||
WOODPECKER_FORGE: string | undefined;
|
||||
WOODPECKER_FORGE: 'github' | 'gitlab' | 'gitea' | 'bitbucket' | undefined;
|
||||
WOODPECKER_ROOT_PATH: string | undefined;
|
||||
WOODPECKER_ENABLE_SWAGGER: boolean | undefined;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
|
@ -31,48 +31,32 @@ import WoodpeckerLogo from '~/assets/logo.svg?component';
|
|||
import Button from '~/components/atomic/Button.vue';
|
||||
import useAuthentication from '~/compositions/useAuthentication';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Login',
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const authentication = useAuthentication();
|
||||
const errorMessage = ref<string>();
|
||||
const i18n = useI18n();
|
||||
|
||||
components: {
|
||||
Button,
|
||||
WoodpeckerLogo,
|
||||
},
|
||||
function doLogin() {
|
||||
const url = typeof route.query.url === 'string' ? route.query.url : '';
|
||||
authentication.authenticate(url);
|
||||
}
|
||||
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const authentication = useAuthentication();
|
||||
const errorMessage = ref<string>();
|
||||
const i18n = useI18n();
|
||||
const authErrorMessages = {
|
||||
oauth_error: i18n.t('user.oauth_error'),
|
||||
internal_error: i18n.t('user.internal_error'),
|
||||
access_denied: i18n.t('user.access_denied'),
|
||||
};
|
||||
|
||||
function doLogin() {
|
||||
const url = typeof route.query.url === 'string' ? route.query.url : '';
|
||||
authentication.authenticate(url);
|
||||
}
|
||||
onMounted(async () => {
|
||||
if (authentication.isAuthenticated) {
|
||||
await router.replace({ name: 'home' });
|
||||
return;
|
||||
}
|
||||
|
||||
const authErrorMessages = {
|
||||
oauth_error: i18n.t('user.oauth_error'),
|
||||
internal_error: i18n.t('user.internal_error'),
|
||||
access_denied: i18n.t('user.access_denied'),
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
if (authentication.isAuthenticated) {
|
||||
await router.replace({ name: 'home' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (route.query.code) {
|
||||
const code = route.query.code as keyof typeof authErrorMessages;
|
||||
errorMessage.value = authErrorMessages[code];
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
doLogin,
|
||||
errorMessage,
|
||||
};
|
||||
},
|
||||
if (route.query.code) {
|
||||
const code = route.query.code as keyof typeof authErrorMessages;
|
||||
errorMessage.value = authErrorMessages[code];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -8,11 +8,3 @@
|
|||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NotFound',
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -5,38 +5,25 @@
|
|||
<PipelineList :pipelines="pipelines" :repo="repo" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, Ref, toRef } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, toRef } from 'vue';
|
||||
|
||||
import PipelineList from '~/components/repo/pipeline/PipelineList.vue';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RepoBranch',
|
||||
const props = defineProps<{
|
||||
branch: string;
|
||||
}>();
|
||||
|
||||
components: { PipelineList },
|
||||
const branch = toRef(props, 'branch');
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||
if (!repo || !repoPermissions) {
|
||||
throw new Error('Unexpected: "repo" & "repoPermissions" should be provided at this place');
|
||||
}
|
||||
|
||||
props: {
|
||||
branch: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const branch = toRef(props, 'branch');
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||
if (!repo || !repoPermissions) {
|
||||
throw new Error('Unexpected: "repo" & "repoPermissions" should be provided at this place');
|
||||
}
|
||||
|
||||
const allPipelines = inject<Ref<Pipeline[]>>('pipelines');
|
||||
const pipelines = computed(() =>
|
||||
allPipelines?.value.filter((b) => b.branch === branch.value && b.event !== 'pull_request'),
|
||||
);
|
||||
|
||||
return { pipelines, repo };
|
||||
},
|
||||
});
|
||||
const allPipelines = inject<Ref<Pipeline[]>>('pipelines');
|
||||
const pipelines = computed(() =>
|
||||
allPipelines?.value.filter((b) => b.branch === branch.value && b.event !== 'pull_request'),
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -2,27 +2,17 @@
|
|||
<PipelineList :pipelines="pipelines" :repo="repo" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, Ref } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { inject, Ref } from 'vue';
|
||||
|
||||
import PipelineList from '~/components/repo/pipeline/PipelineList.vue';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RepoPipelines',
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||
if (!repo || !repoPermissions) {
|
||||
throw new Error('Unexpected: "repo" & "repoPermissions" should be provided at this place');
|
||||
}
|
||||
|
||||
components: { PipelineList },
|
||||
|
||||
setup() {
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||
if (!repo || !repoPermissions) {
|
||||
throw new Error('Unexpected: "repo" & "repoPermissions" should be provided at this place');
|
||||
}
|
||||
|
||||
const pipelines = inject<Ref<Pipeline[]>>('pipelines');
|
||||
|
||||
return { pipelines, repo };
|
||||
},
|
||||
});
|
||||
const pipelines = inject<Ref<Pipeline[]>>('pipelines');
|
||||
</script>
|
||||
|
|
|
@ -11,12 +11,9 @@ import { computed, inject, Ref, toRef } from 'vue';
|
|||
import PipelineList from '~/components/repo/pipeline/PipelineList.vue';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps({
|
||||
pullRequest: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const props = defineProps<{
|
||||
pullRequest: string;
|
||||
}>();
|
||||
const pullRequest = toRef(props, 'pullRequest');
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||
|
|
|
@ -17,13 +17,7 @@
|
|||
<a v-if="badgeUrl" :href="badgeUrl" target="_blank" class="ml-2">
|
||||
<img :src="badgeUrl" />
|
||||
</a>
|
||||
<IconButton :href="repo.link_url" :title="$t('repo.open_in_forge')">
|
||||
<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'" name="bitbucket" />
|
||||
<Icon v-else name="repo" />
|
||||
</IconButton>
|
||||
<IconButton :href="repo.link_url" :title="$t('repo.open_in_forge')" :icon="forge ?? 'repo'" />
|
||||
<IconButton
|
||||
v-if="repoPermissions.admin"
|
||||
:to="{ name: 'repo-settings' }"
|
||||
|
@ -59,7 +53,6 @@ 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';
|
||||
|
|
|
@ -9,26 +9,14 @@
|
|||
</Panel>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, Ref } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { inject, Ref } from 'vue';
|
||||
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import { Pipeline } from '~/lib/api/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PipelineChangedFiles',
|
||||
|
||||
components: {
|
||||
Panel,
|
||||
},
|
||||
|
||||
setup() {
|
||||
const pipeline = inject<Ref<Pipeline>>('pipeline');
|
||||
if (!pipeline) {
|
||||
throw new Error('Unexpected: "pipeline" should be provided at this place');
|
||||
}
|
||||
|
||||
return { pipeline };
|
||||
},
|
||||
});
|
||||
const pipeline = inject<Ref<Pipeline>>('pipeline');
|
||||
if (!pipeline) {
|
||||
throw new Error('Unexpected: "pipeline" should be provided at this place');
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -11,51 +11,38 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, onMounted, Ref, ref, watch } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted, Ref, ref, watch } from 'vue';
|
||||
|
||||
import SyntaxHighlight from '~/components/atomic/SyntaxHighlight';
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { Pipeline, PipelineConfig, Repo } from '~/lib/api/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PipelineConfig',
|
||||
const pipeline = inject<Ref<Pipeline>>('pipeline');
|
||||
const apiClient = useApiClient();
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
if (!repo || !pipeline) {
|
||||
throw new Error('Unexpected: "repo" & "pipeline" should be provided at this place');
|
||||
}
|
||||
|
||||
components: {
|
||||
Panel,
|
||||
SyntaxHighlight,
|
||||
},
|
||||
const pipelineConfigs = ref<PipelineConfig[]>();
|
||||
async function loadPipelineConfig() {
|
||||
if (!repo || !pipeline) {
|
||||
throw new Error('Unexpected: "repo" & "pipeline" should be provided at this place');
|
||||
}
|
||||
|
||||
setup() {
|
||||
const pipeline = inject<Ref<Pipeline>>('pipeline');
|
||||
const apiClient = useApiClient();
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
if (!repo || !pipeline) {
|
||||
throw new Error('Unexpected: "repo" & "pipeline" should be provided at this place');
|
||||
}
|
||||
pipelineConfigs.value = (await apiClient.getPipelineConfig(repo.value.id, pipeline.value.number)).map((i) => ({
|
||||
...i,
|
||||
data: atob(i.data),
|
||||
}));
|
||||
}
|
||||
|
||||
const pipelineConfigs = ref<PipelineConfig[]>();
|
||||
async function loadPipelineConfig() {
|
||||
if (!repo || !pipeline) {
|
||||
throw new Error('Unexpected: "repo" & "pipeline" should be provided at this place');
|
||||
}
|
||||
onMounted(() => {
|
||||
loadPipelineConfig();
|
||||
});
|
||||
|
||||
pipelineConfigs.value = (await apiClient.getPipelineConfig(repo.value.id, pipeline.value.number)).map((i) => ({
|
||||
...i,
|
||||
data: atob(i.data),
|
||||
}));
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadPipelineConfig();
|
||||
});
|
||||
|
||||
watch(pipeline, () => {
|
||||
loadPipelineConfig();
|
||||
});
|
||||
|
||||
return { pipelineConfigs };
|
||||
},
|
||||
watch(pipeline, () => {
|
||||
loadPipelineConfig();
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Reference in a new issue