Use Vue setup directive (#2165)

This commit is contained in:
qwerty287 2023-08-08 12:22:39 +02:00 committed by GitHub
parent 9cae5709f9
commit 3bdeb47d8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 582 additions and 981 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 `[![status-badge](${baseUrl}${badgeUrl.value})](${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 `[![status-badge](${baseUrl}${badgeUrl.value})](${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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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;
}

View file

@ -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>

View file

@ -8,11 +8,3 @@
>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'NotFound',
});
</script>

View file

@ -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>

View file

@ -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>

View file

@ -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');

View file

@ -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';

View file

@ -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>

View file

@ -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>