align view and fix linting

This commit is contained in:
Robert Kaussow
2025-10-12 20:49:56 +02:00
parent 95d0285e91
commit fb464e16f0
11 changed files with 82 additions and 91 deletions

View File

@@ -15,8 +15,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.9
// protoc v6.32.0
// protoc-gen-go v1.36.10
// protoc v6.31.1
// source: woodpecker.proto
package proto

View File

@@ -16,7 +16,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.32.0
// - protoc v6.31.1
// source: woodpecker.proto
package proto

View File

@@ -66,10 +66,14 @@ func PostParameter(c *gin.Context) {
return
}
parameter, err := parameterService.ParameterFind(repo, in.Name)
if err != nil {
handleDBError(c, err)
return
}
c.JSON(http.StatusOK, parameter)
}
// PatchParameter updates an existing parameter by ID
// PatchParameter updates an existing parameter by ID.
func PatchParameter(c *gin.Context) {
repo := session.Repo(c)
paramID, err := strconv.ParseInt(c.Param("parameter"), 10, 64)
@@ -119,6 +123,10 @@ func PatchParameter(c *gin.Context) {
return
}
parameter, err := parameterService.ParameterFindByID(repo, paramID)
if err != nil {
handleDBError(c, err)
return
}
c.JSON(http.StatusOK, parameter)
}

View File

@@ -36,7 +36,7 @@ func (d *db) ParameterFindByID(repo *model.Repo, id int64) (*model.Parameter, er
return d.store.ParameterFindByID(repo, id)
}
func (d *db) ParameterFindByNameAndBranch(repo *model.Repo, name string, branch string) (*model.Parameter, error) {
func (d *db) ParameterFindByNameAndBranch(repo *model.Repo, name, branch string) (*model.Parameter, error) {
return d.store.ParameterFindByNameAndBranch(repo, name, branch)
}

View File

@@ -21,7 +21,7 @@ type Service interface {
// Repository parameters
ParameterFind(*model.Repo, string) (*model.Parameter, error)
ParameterFindByID(*model.Repo, int64) (*model.Parameter, error)
ParameterFindByNameAndBranch(repo *model.Repo, name string, branch string) (*model.Parameter, error)
ParameterFindByNameAndBranch(repo *model.Repo, name, branch string) (*model.Parameter, error)
ParameterList(*model.Repo) ([]*model.Parameter, error)
ParameterCreate(*model.Repo, *model.Parameter) error
ParameterUpdate(*model.Repo, *model.Parameter) error

View File

@@ -28,7 +28,7 @@ func (s storage) ParameterFindByID(repo *model.Repo, id int64) (*model.Parameter
return parameter, wrapGet(s.engine.Where("parameter_repo_id = ? AND parameter_id = ?", repo.ID, id).Get(parameter))
}
func (s storage) ParameterFindByNameAndBranch(repo *model.Repo, name string, branch string) (*model.Parameter, error) {
func (s storage) ParameterFindByNameAndBranch(repo *model.Repo, name, branch string) (*model.Parameter, error) {
parameter := new(model.Parameter)
return parameter, wrapGet(s.engine.Where("parameter_repo_id = ? AND parameter_name = ? AND parameter_branch = ?", repo.ID, name, branch).Get(parameter))
}

View File

@@ -2,26 +2,17 @@
<div v-if="innerParameter" class="space-y-4">
<form @submit.prevent="handleSubmit">
<InputField v-slot="{ id }" :label="$t('parameters.name')">
<TextField
:id="id"
v-model="innerParameter.name"
:placeholder="$t('parameters.name')"
required
/>
<TextField :id="id" v-model="innerParameter.name" :placeholder="$t('parameters.name')" required />
</InputField>
<InputField v-slot="{ id }" :label="$t('parameters.branch')">
<select
:id="id"
v-model="innerParameter.branch"
class="block w-full rounded-md border border-wp-control-neutral-200 bg-wp-control-neutral-100 px-3 py-2 text-sm text-wp-text-100"
class="border-wp-control-neutral-200 bg-wp-control-neutral-100 text-wp-text-100 block w-full rounded-md border px-3 py-2 text-sm"
>
<option value="*">{{ $t('parameters.any_branch') }}</option>
<option
v-for="branch in branches"
:key="branch"
:value="branch"
>
<option v-for="branch in branches" :key="branch" :value="branch">
{{ branch }}
</option>
</select>
@@ -31,7 +22,7 @@
<select
:id="id"
v-model="innerParameter.type"
class="block w-full rounded-md border border-wp-control-neutral-200 bg-wp-control-neutral-100 px-3 py-2 text-sm text-wp-text-100"
class="border-wp-control-neutral-200 bg-wp-control-neutral-100 text-wp-text-100 block w-full rounded-md border px-3 py-2 text-sm"
required
>
<option v-for="type in parameterTypes" :key="type" :value="type">
@@ -43,11 +34,7 @@
<template v-if="innerParameter.type">
<InputField v-if="showDefaultValue" v-slot="{ id }" :label="$t('parameters.default_value')">
<template v-if="isBoolean">
<Checkbox
:id="id"
v-model="booleanValue"
:label="$t('parameters.set_by_default')"
/>
<Checkbox :id="id" v-model="booleanValue" :label="$t('parameters.set_by_default')" />
</template>
<template v-else-if="isPassword">
<TextField
@@ -66,41 +53,28 @@
:lines="5"
:placeholder="$t('parameters.choices_placeholder')"
/>
<span class="mt-1 text-sm text-wp-text-alt-100">{{ $t('parameters.choices_help') }}</span>
<span class="text-wp-text-alt-100 mt-1 text-sm">{{ $t('parameters.choices_help') }}</span>
</template>
<template v-else-if="isText">
<TextField
:id="id"
v-model="innerParameter.default_value"
:lines="3"
class="text-sm"
/>
<TextField :id="id" v-model="innerParameter.default_value" :lines="3" class="text-sm" />
</template>
<template v-else>
<TextField
:id="id"
v-model="innerParameter.default_value"
class="text-sm"
/>
<TextField :id="id" v-model="innerParameter.default_value" class="text-sm" />
</template>
</InputField>
<InputField v-if="showTrimString" v-slot="{ id }" :label="$t('parameters.trim_string')">
<Checkbox
:id="id"
v-model="innerParameter.trim_string"
:model-value="innerParameter.trim_string ?? false"
:label="innerParameter.trim_string ? 'true' : 'false'"
@update:model-value="(val) => (innerParameter.trim_string = val)"
/>
</InputField>
</template>
<InputField v-slot="{ id }" :label="$t('parameters.description')">
<TextField
:id="id"
v-model="innerParameter.description"
:lines="3"
class="text-sm"
/>
<TextField :id="id" v-model="innerParameter.description" :lines="3" class="text-sm" />
</InputField>
<div class="flex gap-2">
@@ -155,18 +129,23 @@ const booleanValue = ref(props.modelValue.default_value === 'true');
const isBoolean = computed(() => innerParameter.value.type === ParameterType.Boolean);
const isPassword = computed(() => innerParameter.value.type === ParameterType.Password);
const isText = computed(() => innerParameter.value.type === ParameterType.Text);
const isChoice = computed(() =>
innerParameter.value.type === ParameterType.SingleChoice ||
innerParameter.value.type === ParameterType.MultipleChoice
const isChoice = computed(
() =>
innerParameter.value.type === ParameterType.SingleChoice ||
innerParameter.value.type === ParameterType.MultipleChoice,
);
const showDefaultValue = computed(() => innerParameter.value.type !== undefined);
const showTrimString = computed(() => !isBoolean.value && !isPassword.value);
// Update local copy when prop changes
watch(() => props.modelValue, (newVal) => {
innerParameter.value = { ...newVal };
booleanValue.value = innerParameter.value.default_value === 'true';
}, { deep: true });
watch(
() => props.modelValue,
(newVal) => {
innerParameter.value = { ...newVal };
booleanValue.value = innerParameter.value.default_value === 'true';
},
{ deep: true },
);
// Watch boolean value changes to update innerParameter
watch(booleanValue, (newVal) => {
@@ -176,24 +155,28 @@ watch(booleanValue, (newVal) => {
});
// Convert default_value to proper type when parameter type changes
watch(() => innerParameter.value.type, (newType) => {
// Don't override existing values when editing
if (props.existingParameter) {
return;
}
watch(
() => innerParameter.value.type,
(newType) => {
// Don't override existing values when editing
if (props.existingParameter) {
return;
}
if (newType === ParameterType.Boolean) {
innerParameter.value.default_value = 'false';
innerParameter.value.trim_string = false;
booleanValue.value = false;
} else if (newType === ParameterType.SingleChoice || newType === ParameterType.MultipleChoice) {
innerParameter.value.default_value = '';
innerParameter.value.trim_string = true;
} else {
innerParameter.value.default_value = '';
innerParameter.value.trim_string = true;
}
}, { immediate: true });
if (newType === ParameterType.Boolean) {
innerParameter.value.default_value = 'false';
innerParameter.value.trim_string = false;
booleanValue.value = false;
} else if (newType === ParameterType.SingleChoice || newType === ParameterType.MultipleChoice) {
innerParameter.value.default_value = '';
innerParameter.value.trim_string = true;
} else {
innerParameter.value.default_value = '';
innerParameter.value.trim_string = true;
}
},
{ immediate: true },
);
// Load branches when component is mounted
async function loadBranches() {
@@ -201,7 +184,7 @@ async function loadBranches() {
try {
const branchList = await apiClient.getRepoBranches(repo.value.id);
branches.value = branchList.map(b => b);
branches.value = branchList.map((b) => b);
} catch (error) {
console.error('Failed to load branches:', error);
}
@@ -231,5 +214,4 @@ function handleSubmit() {
// Emit the save event with the parameter data
emit('save', innerParameter.value);
}
</script>

View File

@@ -1,39 +1,45 @@
<template>
<div class="flex flex-col gap-4">
<ListItem v-for="parameter in parameters" :key="parameter.name" class="flex flex-col gap-2 !bg-wp-background-200 dark:!bg-wp-background-100">
<div class="flex items-center justify-between gap-4">
<ListItem
v-for="parameter in parameters"
:key="parameter.name"
class="bg-wp-background-200! dark:bg-wp-background-100! flex flex-col gap-2"
>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<h3 class="font-bold">{{ parameter.name }}</h3>
<span>{{ parameter.name }}</span>
<div class="flex items-center gap-1">
<Icon name="push" class="h-4 w-4" />
<Icon name="branch" class="h-4 w-4" />
<span>{{ parameter.branch }}</span>
</div>
</div>
<div class="flex gap-2">
<div class="md:display-unset ml-auto hidden space-x-2">
<Badge :label="$t(`parameters.types.${parameter.type}`)" />
</div>
<div class="md:display-unset ml-auto hidden">
<Badge :value="$t(`parameters.types.${parameter.type}`)" />
</div>
<div class="flex items-center gap-2">
<IconButton
:title="$t('parameters.edit')"
icon="edit"
class="h-8 w-8 md:ml-2"
@click="$emit('edit', parameter)"
/>
<IconButton
:title="$t('parameters.delete')"
icon="trash"
class="hover:text-wp-error-100 h-8 w-8"
@click="$emit('delete', parameter)"
/>
</div>
</div>
<p v-if="parameter.description" class="text-sm text-wp-text-alt-100">
<div v-if="parameter.description" class="text-wp-text-alt-100 text-sm">
{{ parameter.description }}
</p>
</div>
</ListItem>
</div>
</template>
<script lang="ts" setup>
import Badge from "~/components/atomic/Badge.vue";
import Badge from '~/components/atomic/Badge.vue';
import Icon from '~/components/atomic/Icon.vue';
import IconButton from '~/components/atomic/IconButton.vue';
import ListItem from '~/components/atomic/ListItem.vue';

View File

@@ -2,13 +2,13 @@ export * from './agent';
export * from './cron';
export * from './forge';
export * from './org';
export * from './parameter';
export * from './pipeline';
export * from './pipelineConfig';
export * from './pull_request';
export * from './queue';
export * from './registry';
export * from './repo';
export * from './parameter';
export * from './secret';
export * from './user';
export * from './webhook';

View File

@@ -4,7 +4,7 @@ export enum ParameterType {
MultipleChoice = 'multiple_choice',
String = 'string',
Text = 'text',
Password = 'password'
Password = 'password',
}
export interface Parameter {

View File

@@ -7,12 +7,7 @@
start-icon="back"
@click="selectedParameter = undefined"
/>
<Button
v-else
:text="$t('parameters.add')"
start-icon="plus"
@click="showAddParameter()"
/>
<Button v-else :text="$t('parameters.add')" start-icon="plus" @click="showAddParameter()" />
</template>
<ParameterList
@@ -44,11 +39,11 @@ import Button from '~/components/atomic/Button.vue';
import Settings from '~/components/layout/Settings.vue';
import ParameterEdit from '~/components/parameters/ParameterEdit.vue';
import ParameterList from '~/components/parameters/ParameterList.vue';
import useApiClient from '~/compositions/useApiClient';
import { useAsyncAction } from '~/compositions/useAsyncAction';
import useNotifications from '~/compositions/useNotifications';
import type { Parameter, Repo } from '~/lib/api/types';
import { ParameterType } from '~/lib/api/types';
import useApiClient from '~/compositions/useApiClient';
const apiClient = useApiClient();
const notifications = useNotifications();
@@ -100,7 +95,7 @@ const { doSubmit: deleteParameter, isLoading: isDeleting } = useAsyncAction(asyn
throw new Error("Unexpected: Can't load repo");
}
await apiClient.deleteParameter(repo.value, _parameter.id);
await apiClient.deleteParameter(repo.value, Number(_parameter.id));
notifications.notify({ title: i18n.t('parameters.deleted'), type: 'success' });
await resetPage();
});