1
0
mirror of https://codeberg.org/crowci/crow.git synced 2025-04-18 04:44:01 +03:00

chore: format web/

This commit is contained in:
pat-s 2025-02-12 23:10:59 +01:00
parent 47dfcd89db
commit 648416613a
No known key found for this signature in database
GPG Key ID: 3C6318841EF78925
99 changed files with 1450 additions and 1347 deletions

View File

@ -14,6 +14,7 @@
"Makefile",
"Justfile",
"venv",
"site"
"site",
".vue"
]
}

View File

@ -2,17 +2,26 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="alternate icon" type="image/png" href="/favicons/favicon-light-default.png" id="favicon-png" />
<link rel="alternate icon" type="image/png" href="/favicons/favicon-dark-default.png" id="favicon-png-dark" />
<link rel="icon" type="image/svg+xml" href="/favicons/favicon-light-default.svg" id="favicon-svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#65a30d" />
<title>Crow CI</title>
<script type="" src="/web-config.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="application/javascript" src="/assets/custom.js"></script>
</body>
</html>

View File

@ -1,19 +1,21 @@
<template>
<div class="app m-auto flex h-full w-full flex-col bg-wp-background-200 dark:bg-wp-background-100">
<router-view v-if="blank" />
<template v-else>
<Navbar />
<router-view v-if="blank" /> <template v-else
> <Navbar />
<main class="relative flex h-full min-h-0">
<div id="scroll-component" class="flex flex-grow flex-col overflow-y-auto">
<router-view />
</div>
<transition name="slide-right">
<PipelineFeedSidebar class="absolute bottom-0 right-0 top-0 w-full max-w-80 border-l shadow-md xl:max-w-96" />
</transition>
<div id="scroll-component" class="flex flex-grow flex-col overflow-y-auto"> <router-view /> </div>
<transition name="slide-right"
> <PipelineFeedSidebar
class="absolute bottom-0 right-0 top-0 w-full max-w-80 border-l shadow-md xl:max-w-96"
/> </transition
>
</main>
</template>
<notifications position="bottom right" />
</template
> <notifications position="bottom right" />
</div>
</template>
<script lang="ts" setup>

View File

@ -1,22 +1,33 @@
<template>
<div v-if="stats" class="flex justify-center">
<div
class="w-full rounded-md border border-wp-background-300 bg-wp-background-200 px-5 py-5 text-wp-text-100 shadow-md dark:bg-wp-background-100"
>
<div class="flex w-full">
<h3 class="flex-1 text-lg font-semibold uppercase leading-tight">
{{ $t('admin.settings.queue.stats.completed_count') }}
{{ $t('admin.settings.queue.stats.completed_count') }}
</h3>
</div>
<div class="relative overflow-hidden transition-all duration-500">
<div>
<div class="pb-4 lg:pb-6">
<h4 class="inline-block text-2xl font-semibold leading-tight lg:text-3xl">
{{ stats.completed_count }}
</h4>
<h4 class="inline-block text-2xl font-semibold leading-tight lg:text-3xl"> {{ stats.completed_count }} </h4>
</div>
<div v-if="total > 0" class="pb-4 lg:pb-6">
<div class="flex h-3 overflow-hidden rounded-full transition-all duration-500">
<div
v-for="item in data"
:key="item.key"
@ -24,30 +35,42 @@
:class="`${item.color}`"
:style="{ width: `${item.percentage}%` }"
>
&nbsp;
&nbsp;
</div>
</div>
</div>
<div class="-mx-4 flex sm:flex-wrap">
<div
v-for="(item, index) in data"
:key="item.key"
class="px-4 sm:w-full md:w-1/4"
:class="{ 'border-gray-300 dark:border-gray-600 md:border-l': index !== 0 }"
>
<div class="overflow-hidden text-ellipsis whitespace-nowrap text-sm">
<span class="mr-1 inline-block h-2 w-2 rounded-full align-middle" :class="`${item.color}`">&nbsp;</span>
<span class="align-middle">{{ item.label }}</span>
</div>
<div class="text-lg font-medium">
{{ item.value }}
<span class="mr-1 inline-block h-2 w-2 rounded-full align-middle" :class="`${item.color}`">&nbsp;</span
> <span class="align-middle">{{ item.label }}</span
>
</div>
<div class="text-lg font-medium"> {{ item.value }} </div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,77 +1,60 @@
<template>
<form @submit.prevent="$emit('save')">
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.name.name')">
<TextField :id="id" v-model="agent.name" :placeholder="$t('admin.settings.agents.name.placeholder')" required />
</InputField>
<InputField :label="$t('admin.settings.agents.no_schedule.name')">
<Checkbox
<form @submit.prevent="$emit('save')">
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.name.name')"
> <TextField :id="id" v-model="agent.name" :placeholder="$t('admin.settings.agents.name.placeholder')" required />
</InputField
> <InputField :label="$t('admin.settings.agents.no_schedule.name')"
> <Checkbox
:model-value="agent.no_schedule || false"
:label="$t('admin.settings.agents.no_schedule.placeholder')"
@update:model-value="updateAgent({ no_schedule: $event })"
/>
</InputField>
<template v-if="isEditingAgent">
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.token')">
<TextField :id="id" v-model="agent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.id')">
<TextField :id="id" :model-value="agent.id?.toString()" disabled />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.backend.backend')" :docs-url="backendDocsUrl">
<TextField :id="id" v-model="agent.backend" disabled />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.platform.platform')">
<TextField :id="id" v-model="agent.platform" disabled />
</InputField>
<InputField
/> </InputField
> <template v-if="isEditingAgent"
> <InputField v-slot="{ id }" :label="$t('admin.settings.agents.token')"
> <TextField :id="id" v-model="agent.token" :placeholder="$t('admin.settings.agents.token')" disabled />
</InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.agents.id')"
> <TextField :id="id" :model-value="agent.id?.toString()" disabled /> </InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.agents.backend.backend')" :docs-url="backendDocsUrl"
> <TextField :id="id" v-model="agent.backend" disabled /> </InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.agents.platform.platform')"
> <TextField :id="id" v-model="agent.platform" disabled /> </InputField
> <InputField
v-if="agent.custom_labels && Object.keys(agent.custom_labels).length > 0"
v-slot="{ id }"
:label="$t('admin.settings.agents.custom_labels.custom_labels')"
>
<span class="text-wp-text-alt-100">{{ $t('admin.settings.agents.custom_labels.desc') }}</span>
<TextField :id="id" :model-value="formatCustomLabels(agent.custom_labels)" disabled />
</InputField>
<InputField
> <span class="text-wp-text-alt-100">{{ $t('admin.settings.agents.custom_labels.desc') }}</span
> <TextField :id="id" :model-value="formatCustomLabels(agent.custom_labels)" disabled /> </InputField
> <InputField
v-slot="{ id }"
:label="$t('admin.settings.agents.capacity.capacity')"
docs-url="docs/next/administration/agent-config#woodpecker_max_workflows"
>
<span class="text-wp-text-alt-100">{{ $t('admin.settings.agents.capacity.desc') }}</span>
<TextField :id="id" :model-value="agent.capacity?.toString()" disabled />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.version')">
<TextField :id="id" :model-value="agent.version" disabled />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.last_contact')">
<TextField
> <span class="text-wp-text-alt-100">{{ $t('admin.settings.agents.capacity.desc') }}</span
> <TextField :id="id" :model-value="agent.capacity?.toString()" disabled /> </InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.agents.version')"
> <TextField :id="id" :model-value="agent.version" disabled /> </InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.agents.last_contact')"
> <TextField
:id="id"
:model-value="
agent.last_contact ? date.timeAgo(agent.last_contact * 1000) : $t('admin.settings.agents.never')
"
disabled
/>
</InputField>
</template>
/> </InputField
> </template
>
<div class="flex gap-2">
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" />
<Button
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" /> <Button
:is-loading="isSaving"
type="submit"
color="default"
:text="isEditingAgent ? $t('admin.settings.agents.save') : $t('admin.settings.agents.add')"
/>
</div>
</form>
</template>
<script lang="ts" setup>

View File

@ -1,46 +1,44 @@
<template>
<div v-if="!props.loading" class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="agent in props.agents"
:key="agent.id"
class="items-center !bg-wp-background-200 shadow-md dark:!bg-wp-background-100"
>
<span>{{ agent.name || `Agent ${agent.id}` }}</span>
<span class="ml-auto">
<span class="hidden space-x-2 md:inline-block">
<Badge
> <span>{{ agent.name || `Agent ${agent.id}` }}</span
> <span class="ml-auto"
> <span class="hidden space-x-2 md:inline-block"
> <Badge
v-if="props.isAdmin === true && agent.org_id !== -1"
:label="$t('admin.settings.agents.org.badge')"
:value="agent.org_id"
/>
<Badge v-if="agent.platform" :label="$t('admin.settings.agents.platform.badge')" :value="agent.platform" />
/> <Badge v-if="agent.platform" :label="$t('admin.settings.agents.platform.badge')" :value="agent.platform" />
<Badge v-if="agent.backend" :label="$t('admin.settings.agents.backend.badge')" :value="agent.backend" />
<Badge v-if="agent.capacity" :label="$t('admin.settings.agents.capacity.badge')" :value="agent.capacity" />
</span>
<span title="Last contact" class="ml-2">{{
</span
> <span title="Last contact" class="ml-2">{{
agent.last_contact ? date.timeAgo(agent.last_contact * 1000) : $t('admin.settings.agents.never')
}}</span>
</span>
<IconButton
}}</span
> </span
> <IconButton
icon="edit"
:title="$t('admin.settings.agents.edit_agent')"
class="ml-2 h-8 w-8"
@click="$emit('edit', agent)"
/>
<IconButton
/> <IconButton
icon="trash"
:title="$t('admin.settings.agents.delete_agent')"
class="ml-2 h-8 w-8 hover:text-wp-error-100"
:is-loading="props.isDeleting"
@click="$emit('delete', agent)"
/>
</ListItem>
/> </ListItem
>
<div v-if="props.agents?.length === 0" class="ml-2">{{ $t('admin.settings.agents.none') }}</div>
</div>
<div v-else class="flex justify-center">
<Icon name="loading" class="animate-spin" />
</div>
<div v-else class="flex justify-center"> <Icon name="loading" class="animate-spin" /> </div>
</template>
<script lang="ts" setup>

View File

@ -1,16 +1,13 @@
<template>
<Settings :title="$t('admin.settings.agents.agents')" :description>
<template #headerActions>
<Button
<Settings :title="$t('admin.settings.agents.agents')" :description
> <template #headerActions
> <Button
v-if="selectedAgent"
:text="$t('admin.settings.agents.show')"
start-icon="back"
@click="selectedAgent = undefined"
/>
<Button v-else :text="$t('admin.settings.agents.add')" start-icon="plus" @click="showAddAgent" />
</template>
<AgentList
/> <Button v-else :text="$t('admin.settings.agents.add')" start-icon="plus" @click="showAddAgent" /> </template
> <AgentList
v-if="!selectedAgent"
:loading="loading"
:agents="agents"
@ -18,16 +15,15 @@
:is-admin="isAdmin"
@edit="editAgent"
@delete="deleteAgent"
/>
<AgentForm
/> <AgentForm
v-else
v-model="selectedAgent"
:is-editing-agent="isEditingAgent"
:is-saving="isSaving"
@save="saveAgent"
@cancel="selectedAgent = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,22 +1,19 @@
<template>
<span class="inline-flex text-xs font-medium">
<span
<span class="inline-flex text-xs font-medium"
> <span
class="flex items-center rounded-l-full border border-wp-custom-highlight-100 py-0.5 pl-2 pr-1 text-gray-300 text-wp-text-100"
:class="{
'rounded-r-full pr-2': value === undefined,
'!border-wp-error-100': label === 'global secret',
'!border-wp-hint-warn-200': label === 'organization secret',
}"
>
{{ label }}
</span>
<span
> {{ label }} </span
> <span
v-if="value !== undefined"
class="flex items-center rounded-r-full border border-l-0 border-wp-custom-highlight-100 py-0.5 pl-1 pr-2"
>
{{ value }}
</span>
</span>
> {{ value }} </span
> </span
>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<component
<component
:is="to === undefined ? 'button' : httpLink ? 'a' : 'router-link'"
v-bind="btnAttrs"
class="relative flex flex-shrink-0 cursor-pointer items-center overflow-hidden whitespace-nowrap rounded-md border border-wp-custom-highlight-100 px-2 py-1 shadow-sm transition-all duration-150 hover:border-wp-orange-300 hover:bg-wp-orange-300 disabled:cursor-not-allowed disabled:opacity-50 dark:hover:border-orange-600 dark:hover:bg-orange-600"
@ -15,16 +15,14 @@
}"
:title="title"
:disabled="disabled"
>
<slot>
<Icon
> <slot
> <Icon
v-if="startIcon"
:name="startIcon"
class="!h-5 !w-5 dark:text-wp-text-200"
:class="{ invisible: isLoading, 'mr-1': text }"
/>
<span :class="{ invisible: isLoading }">{{ text }}</span>
<Icon v-if="endIcon" :name="endIcon" class="ml-2 h-6 w-6" :class="{ invisible: isLoading }" />
/> <span :class="{ invisible: isLoading }">{{ text }}</span
> <Icon v-if="endIcon" :name="endIcon" class="ml-2 h-6 w-6" :class="{ invisible: isLoading }" />
<div
v-if="isLoading"
class="absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center"
@ -34,10 +32,11 @@
'bg-wp-error-200': color === 'red',
}"
>
<Icon name="spinner" class="h-5 w-5" />
<Icon name="spinner" class="h-5 w-5" />
</div>
</slot>
</component>
</slot
> </component
>
</template>
<script lang="ts" setup>

View File

@ -1,9 +1,8 @@
<template>
<span
<span
class="inline-block min-w-5 rounded-full bg-wp-background-300 px-1.5 py-0.5 text-center text-xs font-bold leading-4 text-wp-text-100 dark:bg-wp-background-50"
> {{ value }} </span
>
{{ value }}
</span>
</template>
<script lang="ts" setup>

View File

@ -1,12 +1,11 @@
<template>
<a
<a
:href="`${docsUrl}`"
:title="$t('documentation_for', { topic })"
target="_blank"
class="hover:text-wp-link-300 cursor-pointer text-wp-custom-highlight-100"
> <Icon name="question" class="!h-5 !w-5" /> </a
>
<Icon name="question" class="!h-5 !w-5" />
</a>
</template>
<script lang="ts" setup>

View File

@ -1,12 +1,14 @@
<template>
<div
class="flex items-center gap-2 rounded-md border border-l-4 border-solid border-wp-error-200 bg-wp-error-100 p-2 text-white"
>
<Icon v-if="!textOnly" name="alert" />
<slot>
<span class="whitespace-pre">{{ text }}</span>
</slot>
<Icon v-if="!textOnly" name="alert" /> <slot
> <span class="whitespace-pre">{{ text }}</span
> </slot
>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,69 +1,118 @@
<!-- cSpell:ignore radiobox timelapse -->
<template>
<SvgIcon v-if="name === 'duration'" :path="mdiTimerOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'since'" :path="mdiClockTimeEightOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'push'" :path="mdiSourceBranch" size="1.3rem" />
<SvgIcon v-else-if="name === 'pull-request'" :path="mdiSourcePull" size="1.3rem" />
<SvgIcon v-else-if="name === 'pull-request-closed'" :path="mdiSourceMerge" size="1.3rem" />
<SvgIcon v-else-if="name === 'manual-pipeline'" :path="mdiGestureTap" size="1.3rem" />
<SvgIcon v-else-if="name === 'tag'" :path="mdiTagOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'deployment'" :path="mdiPackageVariant" size="1.3rem" />
<SvgIcon v-else-if="name === 'commit'" :path="mdiSourceCommit" size="1.3rem" />
<SvgIcon v-else-if="name === 'back'" :path="mdiArrowLeft" size="1.3rem" />
<SvgIcon v-else-if="name === 'github'" :path="mdiGithub" size="1.3rem" />
<SvgIcon v-else-if="name === 'repo'" :path="mdiGit" size="1.3rem" />
<SvgIcon v-else-if="name === 'settings'" :path="mdiCog" size="1.3rem" />
<SvgIcon v-else-if="name === 'settings-outline'" :path="mdiCogOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'trash'" :path="mdiTrashCanOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'status-blocked'" :path="mdiPlay" size="1.3rem" />
<SvgIcon v-else-if="name === 'status-declined'" :path="mdiStop" size="1.3rem" />
<SvgIcon v-else-if="name === 'status-killed'" :path="mdiStop" size="1.3rem" />
<SvgIcon v-else-if="name === 'status-failure' || name === 'status-error'" type="mdi" :path="mdiClose" size="1.3rem" />
<SvgIcon v-else-if="name === 'status-pending'" :path="mdiRadioboxBlank" size="1.3rem" />
<SvgIcon
<SvgIcon v-if="name === 'duration'" :path="mdiTimerOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'since'"
:path="mdiClockTimeEightOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'push'" :path="mdiSourceBranch" size="1.3rem" /> <SvgIcon
v-else-if="name === 'pull-request'"
:path="mdiSourcePull"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'pull-request-closed'" :path="mdiSourceMerge" size="1.3rem" /> <SvgIcon
v-else-if="name === 'manual-pipeline'"
:path="mdiGestureTap"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'tag'" :path="mdiTagOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'deployment'"
:path="mdiPackageVariant"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'commit'" :path="mdiSourceCommit" size="1.3rem" /> <SvgIcon
v-else-if="name === 'back'"
:path="mdiArrowLeft"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'github'" :path="mdiGithub" size="1.3rem" /> <SvgIcon
v-else-if="name === 'repo'"
:path="mdiGit"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'settings'" :path="mdiCog" size="1.3rem" /> <SvgIcon
v-else-if="name === 'settings-outline'"
:path="mdiCogOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'trash'" :path="mdiTrashCanOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'status-blocked'"
:path="mdiPlay"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'status-declined'" :path="mdiStop" size="1.3rem" /> <SvgIcon
v-else-if="name === 'status-killed'"
:path="mdiStop"
size="1.3rem"
/> <SvgIcon
v-else-if="name === 'status-failure' || name === 'status-error'"
type="mdi"
:path="mdiClose"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'status-pending'" :path="mdiRadioboxBlank" size="1.3rem" /> <SvgIcon
v-else-if="name === 'status-running' || name === 'status-started'"
type="mdi"
:path="mdiRadioboxIndeterminateVariant"
size="1.3rem"
/>
<SvgIcon v-else-if="name === 'status-skipped'" :path="mdiMinus" size="1.3rem" />
<SvgIcon v-else-if="name === 'status-success'" :path="mdiCheck" size="1.3rem" />
<SvgIcon v-else-if="name === 'alert'" :path="mdiAlertCircle" size="1.3rem" />
<SvgIcon v-else-if="name === 'question'" :path="mdiInformationSlabCircleOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'plus'" :path="mdiPlus" size="1.3rem" />
<SvgIcon v-else-if="name === 'list'" :path="mdiFormatListBulleted" size="1.3rem" />
<SvgIcon v-else-if="name === 'heal'" :path="mdiWrenchCogOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'turn-off'" :path="mdiPower" size="1.3rem" />
<SvgIcon v-else-if="name === 'chevron-right'" :path="mdiChevronRight" size="1.3rem" />
<SvgIcon v-else-if="name === 'close'" :path="mdiClose" size="1.3rem" />
<SvgIcon v-else-if="name === 'edit'" :path="mdiPencilOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'download'" :path="mdiDownloadOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'stopwatch'" :path="mdiAlarm" size="1.3rem" />
<SvgIcon v-else-if="name === 'auto-scroll'" :path="mdiEyeOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'auto-scroll-off'" :path="mdiEyeOffOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'pause'" :path="mdiPause" size="1.3rem" />
<SvgIcon v-else-if="name === 'play'" :path="mdiPlay" size="1.3rem" />
<SvgIcon v-else-if="name === 'play-outline'" :path="mdiPlayOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'dots'" :path="mdiDotsVertical" size="1.3rem" />
/> <SvgIcon v-else-if="name === 'status-skipped'" :path="mdiMinus" size="1.3rem" /> <SvgIcon
v-else-if="name === 'status-success'"
:path="mdiCheck"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'alert'" :path="mdiAlertCircle" size="1.3rem" /> <SvgIcon
v-else-if="name === 'question'"
:path="mdiInformationSlabCircleOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'plus'" :path="mdiPlus" size="1.3rem" /> <SvgIcon
v-else-if="name === 'list'"
:path="mdiFormatListBulleted"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'heal'" :path="mdiWrenchCogOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'turn-off'"
:path="mdiPower"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'chevron-right'" :path="mdiChevronRight" size="1.3rem" /> <SvgIcon
v-else-if="name === 'close'"
:path="mdiClose"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'edit'" :path="mdiPencilOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'download'"
:path="mdiDownloadOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'stopwatch'" :path="mdiAlarm" size="1.3rem" /> <SvgIcon
v-else-if="name === 'auto-scroll'"
:path="mdiEyeOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'auto-scroll-off'" :path="mdiEyeOffOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'pause'"
:path="mdiPause"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'play'" :path="mdiPlay" size="1.3rem" /> <SvgIcon
v-else-if="name === 'play-outline'"
:path="mdiPlayOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'dots'" :path="mdiDotsVertical" size="1.3rem" /> <SvgIcon
v-else-if="name === 'visibility-private'"
:path="mdiLockOutline"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'visibility-internal'" :path="mdiLockOpenOutline" size="1.3rem" /> <SvgIcon
v-else-if="name === 'forgejo'"
:path="siForgejo.path"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'gitea'" :path="siGitea.path" size="1.3rem" /> <SvgIcon
v-else-if="name === 'gitlab'"
:path="mdiGitlab"
size="1.3rem"
/> <SvgIcon v-else-if="name === 'bitbucket' || name === 'bitbucket-dc'" :path="mdiBitbucket" size="1.3rem" /> <svg
v-else-if="name === 'spinner'"
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<SvgIcon v-else-if="name === 'visibility-private'" :path="mdiLockOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'visibility-internal'" :path="mdiLockOpenOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'forgejo'" :path="siForgejo.path" size="1.3rem" />
<SvgIcon v-else-if="name === 'gitea'" :path="siGitea.path" size="1.3rem" />
<SvgIcon v-else-if="name === 'gitlab'" :path="mdiGitlab" size="1.3rem" />
<SvgIcon v-else-if="name === 'bitbucket' || name === 'bitbucket-dc'" :path="mdiBitbucket" size="1.3rem" />
<svg v-else-if="name === 'spinner'" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
fill="currentColor"
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/>
<path
fill="currentColor"
d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"
>
<animateTransform
attributeName="transform"
type="rotate"
@ -71,10 +120,12 @@
values="0 12 12;360 12 12"
repeatCount="indefinite"
/>
</path>
</svg>
</path>
</svg
>
<div v-else-if="name === 'blank'" class="h-6 w-6" />
</template>
<script lang="ts" setup>

View File

@ -1,10 +1,7 @@
<template>
<router-link v-if="to" :to="to" :title="title" :aria-label="title" class="icon-button h-8 w-8">
<slot>
<Icon v-if="icon" :name="icon" />
</slot>
</router-link>
<a
<router-link v-if="to" :to="to" :title="title" :aria-label="title" class="icon-button h-8 w-8"
> <slot> <Icon v-if="icon" :name="icon" /> </slot> </router-link
> <a
v-else-if="href"
:href="href"
:title="title"
@ -12,19 +9,14 @@
class="icon-button"
target="_blank"
rel="noopener noreferrer"
>
<slot>
<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 v-if="icon" :name="icon" />
</slot>
> <slot> <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 v-if="icon" :name="icon" /> </slot>
<div v-if="isLoading" class="absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center">
<Icon name="loading" class="animate-spin" />
<Icon name="loading" class="animate-spin" />
</div>
</button>
</button
>
</template>
<script lang="ts" setup>

View File

@ -1,14 +1,13 @@
<template>
<component
<component
:is="to ? 'router-link' : clickable ? 'button' : 'div'"
:to="to"
class="flex w-full overflow-hidden rounded-md border border-wp-background-400 bg-wp-background-100 p-4 shadow-sm dark:bg-wp-background-200"
:class="{
'cursor-pointer hover:bg-wp-background-300 hover:shadow-sm dark:hover:bg-wp-background-300': clickable || to,
}"
> <slot /> </component
>
<slot />
</component>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,4 @@
<template>
<span v-html="contentHTML" />
</template>
<template> <span v-html="contentHTML" /> </template>
<script setup lang="ts">
import DOMPurify from 'dompurify';

View File

@ -1,7 +1,9 @@
<template>
<svg fill="currentColor" :width="size" :height="size" viewBox="0 0 24 24">
<svg fill="currentColor" :width="size" :height="size" viewBox="0 0 24 24">
<path :d="path" />
</svg>
</svg
>
</template>
<script lang="ts" setup>

View File

@ -1,12 +1,14 @@
<template>
<div
class="flex items-center gap-4 rounded-md border border-l-4 border-solid border-wp-hint-warn-200 bg-wp-hint-warn-100 p-4 font-bold text-wp-text-100"
>
<Icon v-if="!textOnly" name="alert" class="flex-shrink-0 text-wp-hint-warn-200" />
<slot>
<span class="whitespace-pre-wrap">{{ text }}</span>
</slot>
<Icon v-if="!textOnly" name="alert" class="flex-shrink-0 text-wp-hint-warn-200" /> <slot
> <span class="whitespace-pre-wrap">{{ text }}</span
> </slot
>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,7 @@
<template>
<div class="mb-2 flex items-center">
<input
<input
:id="`checkbox-${id}`"
type="checkbox"
class="checkbox relative h-5 w-5 flex-shrink-0 cursor-pointer border border-wp-background-400 bg-wp-control-neutral-100 shadow-sm transition-colors duration-150 checked:border-wp-background-secondary-800 checked:bg-wp-background-secondary-800 focus-visible:border-wp-control-neutral-300 checked:focus-visible:border-wp-control-ok-300"
@ -8,10 +9,13 @@
@click="innerValue = !innerValue"
/>
<div class="ml-4 flex flex-col">
<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>
<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" setup>

View File

@ -1,5 +1,5 @@
<template>
<Checkbox
<Checkbox
v-for="option in options"
:key="option.value"
:model-value="innerValue.includes(option.value)"

View File

@ -1,15 +1,19 @@
<template>
<div class="mb-4 mt-2 flex flex-col">
<div class="mb-2 flex items-center">
<label class="font-bold text-wp-text-100" :for="id" v-bind="$attrs">{{ label }}</label>
<DocsLink v-if="docsUrl" :topic="label" :url="docsUrl" class="ml-2" />
<slot v-else-if="$slots.titleActions" name="titleActions" />
<label class="font-bold text-wp-text-100" :for="id" v-bind="$attrs">{{ label }}</label
> <DocsLink v-if="docsUrl" :topic="label" :url="docsUrl" class="ml-2" /> <slot
v-else-if="$slots.titleActions"
name="titleActions"
/>
</div>
<div v-if="$slots.description" class="mb-2 text-sm text-wp-text-alt-100">
<slot name="description" />
</div>
<slot :id="id" />
<div v-if="$slots.description" class="mb-2 text-sm text-wp-text-alt-100"> <slot name="description" /> </div>
<slot :id="id" />
</div>
</template>
<script lang="ts" setup>

View File

@ -1,7 +1,9 @@
<template>
<div class="flex flex-col gap-2">
<div v-for="(item, index) in displayItems" :key="index" class="flex gap-4">
<TextField
<TextField
:id="`${id}-key-${index}`"
:model-value="item.key"
:placeholder="keyPlaceholder"
@ -10,27 +12,28 @@
isDuplicateKey(item.key, index) || (item.key === '' && index !== displayItems.length - 1),
}"
@update:model-value="updateItem(index, 'key', $event)"
/>
<TextField
/> <TextField
:id="`${id}-value-${index}`"
:model-value="item.value"
:placeholder="valuePlaceholder"
@update:model-value="updateItem(index, 'value', $event)"
/>
<div class="w-10 flex-shrink-0">
<Button
<Button
v-if="index !== displayItems.length - 1"
type="button"
color="red"
class="ml-auto"
:title="deleteTitle"
@click="deleteItem(index)"
> <Icon name="close" /> </Button
>
<Icon name="close" />
</Button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,4 @@
<template>
<TextField v-model="innerValue" :placeholder="placeholder" type="number" />
</template>
<template> <TextField v-model="innerValue" :placeholder="placeholder" type="number" /> </template>
<script lang="ts" setup>
import { computed, toRef } from 'vue';

View File

@ -1,6 +1,7 @@
<template>
<div v-for="option in options" :key="option.value" class="mb-2 flex items-center">
<input
<input
:id="`radio-${id}-${option.value}`"
type="radio"
class="radio relative h-5 w-5 flex-shrink-0 cursor-pointer rounded-full border border-wp-background-400 bg-wp-control-neutral-100 shadow-sm checked:border-wp-background-secondary-800 checked:bg-wp-background-secondary-800 focus-visible:border-wp-control-neutral-300 checked:focus-visible:border-wp-control-ok-300"
@ -9,10 +10,13 @@
@click="innerValue = option.value"
/>
<div class="ml-4 flex flex-col">
<label class="cursor-pointer text-wp-text-100" :for="`radio-${id}-${option.value}`">{{ option.text }}</label>
<span v-if="option.description" class="text-sm text-wp-text-alt-100">{{ option.description }}</span>
<label class="cursor-pointer text-wp-text-100" :for="`radio-${id}-${option.value}`">{{ option.text }}</label
> <span v-if="option.description" class="text-sm text-wp-text-alt-100">{{ option.description }}</span
>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,13 +1,16 @@
<template>
<select
<select
v-model="innerValue"
class="w-full rounded-md border border-wp-background-400 bg-wp-control-neutral-100 px-2 py-1 text-wp-text-100"
>
<option v-if="placeholder" value="" class="hidden">{{ placeholder }}</option>
<option v-for="option in options" :key="option.value" :value="option.value" class="text-wp-text-100">
{{ option.text }}
{{ option.text }}
</option>
</select>
</select
>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<input
<input
v-if="lines === 1"
v-model="innerValue"
class="w-full rounded-md border border-wp-background-400 bg-wp-background-100 px-2 py-1 shadow-md focus-visible:border-wp-control-neutral-300 focus-visible:outline-none"
@ -7,8 +7,7 @@
:disabled="disabled"
:type="type"
:placeholder="placeholder"
/>
<textarea
/> <textarea
v-else
v-model="innerValue"
class="w-full rounded-md border border-wp-background-400 bg-wp-background-100 px-2 py-1 shadow-md focus-visible:border-wp-control-neutral-300 focus-visible:outline-none"

View File

@ -1,7 +1,9 @@
<template>
<div class="mx-auto w-full p-4" :class="{ 'max-w-5xl': !fullWidth && !fillWidth, 'md:px-0': fullWidth }">
<slot />
<slot />
</div>
</template>
<script setup lang="ts">

View File

@ -1,22 +1,21 @@
<template>
<div
class="w-full overflow-hidden rounded-md border border-wp-background-400 bg-wp-background-100 shadow-sm dark:bg-wp-background-200"
>
<component
<component
:is="collapsable ? 'button' : 'div'"
v-if="title"
type="button"
class="flex w-full gap-2 bg-wp-background-300 px-4 py-2 font-bold text-wp-text-100"
@click="_collapsed = !_collapsed"
>
<Icon
> <Icon
v-if="collapsable"
name="chevron-right"
class="h-6 min-w-6 transition-transform duration-150"
:class="{ 'rotate-90 transform': !collapsed }"
/>
{{ title }}
</component>
/> {{ title }} </component
>
<div
:class="{
'max-h-auto': !collapsed,
@ -24,11 +23,13 @@
}"
class="overflow-hidden transition-height duration-150"
>
<div class="w-full p-4 text-wp-text-100">
<slot />
</div>
<div class="w-full p-4 text-wp-text-100"> <slot /> </div>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,16 +1,17 @@
<template>
<!-- overlay -->
<!-- overlay -->
<div
v-if="open"
class="fixed bottom-0 left-0 right-0 top-0 z-40 bg-gray-900 opacity-80 print:hidden"
@click="$emit('close')"
/>
<!-- overlay end -->
<!-- overlay end -->
<div v-if="open" class="fixed inset-0 z-50 m-auto flex max-w-2xl print:hidden">
<div class="shadow-all m-auto flex h-auto flex-col p-2">
<slot />
</div>
<div class="shadow-all m-auto flex h-auto flex-col p-2"> <slot /> </div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,25 +1,27 @@
<template>
<Panel>
<Panel
>
<div class="mb-4 flex flex-col justify-center border-b pb-4 dark:border-wp-custom-highlight-100">
<div class="flex items-center justify-between">
<h1 class="flex items-center gap-1 text-xl text-wp-text-100">
{{ title }}
<DocsLink v-if="docsUrl" :topic="title" :url="docsUrl" />
{{ title }} <DocsLink v-if="docsUrl" :topic="title" :url="docsUrl" />
</h1>
<slot v-if="$slots.titleActions" name="titleActions" />
<slot v-if="$slots.titleActions" name="titleActions" />
</div>
<div class="flex flex-wrap items-center justify-between gap-x-4 gap-y-2">
<p v-if="description" class="text-sm text-wp-text-alt-100">{{ description }}</p>
<div v-if="$slots.headerActions">
<slot name="headerActions" />
</div>
</div>
<slot name="headerEnd" />
</div>
<slot />
</Panel>
<p v-if="description" class="text-sm text-wp-text-alt-100">{{ description }}</p>
<div v-if="$slots.headerActions"> <slot name="headerActions" /> </div>
</div>
<slot name="headerEnd" />
</div>
<slot /> </Panel
>
</template>
<script setup lang="ts">

View File

@ -1,17 +1,18 @@
<template>
<IconButton
<IconButton
:title="pipelineCount > 0 ? `${$t('pipeline_feed')} (${pipelineCount})` : $t('pipeline_feed')"
class="active-pipelines-toggle relative !p-1.5 text-current"
@click="toggle"
>
>
<div v-if="pipelineCount > 0" class="spinner" />
<div
class="z-0 flex h-full w-full items-center justify-center rounded-md border border-wp-custom-highlight-100 bg-white bg-opacity-10 font-bold"
>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
{{ pipelineCount > 9 ? '9+' : pipelineCount }}
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> {{ pipelineCount > 9 ? '9+' : pipelineCount }}
</div>
</IconButton>
</IconButton
>
</template>
<script lang="ts" setup>

View File

@ -1,41 +1,46 @@
<template>
<nav
class="text-neutral-content flex border-wp-background-100 bg-wp-background-secondary-800 p-3 text-wp-primary-text-100"
>
<div class="flex items-center space-x-2">
<router-link :to="{ name: 'home' }" class="-my-2 flex flex-col px-2">
<CrowLogo class="h-8 w-8" />
<!-- <span class="text-center text-xs" :title="version?.current">{{ version?.currentShort }}</span> -->
</router-link>
<router-link v-if="user" :to="{ name: 'repos' }" class="navbar-clickable navbar-link">
<span class="flex md:hidden">{{ $t('repos') }}</span>
<span class="hidden md:flex">{{ $t('repositories') }}</span>
</router-link>
<a href="https://crowci.dev/" target="_blank" class="navbar-clickable navbar-link hidden md:flex">{{
<router-link :to="{ name: 'home' }" class="-my-2 flex flex-col px-2"
> <CrowLogo class="h-8 w-8" /> <!-- <span class="text-center text-xs" :title="version?.current">{{ version?.currentShort }}</span> -->
</router-link
> <router-link v-if="user" :to="{ name: 'repos' }" class="navbar-clickable navbar-link"
> <span class="flex md:hidden">{{ $t('repos') }}</span
> <span class="hidden md:flex">{{ $t('repositories') }}</span
> </router-link
> <a href="https://crowci.dev/" target="_blank" class="navbar-clickable navbar-link hidden md:flex">{{
$t('docs')
}}</a>
<a v-if="enableSwagger" :href="apiUrl" target="_blank" class="navbar-clickable navbar-link hidden md:flex">{{
}}</a
> <a v-if="enableSwagger" :href="apiUrl" target="_blank" class="navbar-clickable navbar-link hidden md:flex">{{
$t('api')
}}</a>
}}</a
>
</div>
<div class="-m-1.5 ml-auto flex items-center space-x-2">
<IconButton
<IconButton
v-if="user?.admin"
class="navbar-icon relative"
:title="$t('settings')"
:to="{ name: 'admin-settings' }"
>
<Icon name="settings" />
> <Icon name="settings" />
<div v-if="version?.needsUpdate" class="absolute right-2 top-2 h-3 w-3 rounded-full bg-wp-error-100" />
</IconButton>
<ActivePipelines v-if="user" class="navbar-icon !p-1.5" />
<IconButton v-if="user" :to="{ name: 'user' }" :title="$t('user.settings.settings')" class="navbar-icon !p-1.5">
<img v-if="user && user.avatar_url" class="rounded-md" :src="`${user.avatar_url}`" />
</IconButton>
<Button v-else :text="$t('login')" :to="`/login?url=${route.fullPath}`" />
</IconButton
> <ActivePipelines v-if="user" class="navbar-icon !p-1.5" /> <IconButton
v-if="user"
:to="{ name: 'user' }"
:title="$t('user.settings.settings')"
class="navbar-icon !p-1.5"
> <img v-if="user && user.avatar_url" class="rounded-md" :src="`${user.avatar_url}`" /> </IconButton
> <Button v-else :text="$t('login')" :to="`/login?url=${route.fullPath}`" />
</div>
</nav>
</template>
<script lang="ts" setup>

View File

@ -1,33 +1,32 @@
<template>
<Popup :open="open" @close="$emit('close')">
<Panel v-if="!loading">
<Popup :open="open" @close="$emit('close')"
> <Panel v-if="!loading"
>
<form @submit.prevent="triggerDeployPipeline">
<span class="text-xl text-wp-text-100">{{
<span class="text-xl text-wp-text-100">{{
$t('repo.deploy_pipeline.title', { pipelineId: pipelineNumber })
}}</span>
<InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.enter_target.title')">
<span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.deploy_pipeline.enter_target.desc') }}</span>
<TextField :id="id" v-model="payload.environment" required />
</InputField>
<InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.enter_task.title')">
<span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.deploy_pipeline.enter_task.desc') }}</span>
<TextField :id="id" v-model="payload.task" />
</InputField>
<InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.variables.title')">
<span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.deploy_pipeline.variables.desc') }}</span>
<KeyValueEditor
}}</span
> <InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.enter_target.title')"
> <span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.deploy_pipeline.enter_target.desc') }}</span
> <TextField :id="id" v-model="payload.environment" required /> </InputField
> <InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.enter_task.title')"
> <span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.deploy_pipeline.enter_task.desc') }}</span
> <TextField :id="id" v-model="payload.task" /> </InputField
> <InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.variables.title')"
> <span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.deploy_pipeline.variables.desc') }}</span
> <KeyValueEditor
:id="id"
v-model="payload.variables"
:key-placeholder="$t('repo.deploy_pipeline.variables.name')"
:value-placeholder="$t('repo.deploy_pipeline.variables.value')"
:delete-title="$t('repo.deploy_pipeline.variables.delete')"
@update:is-valid="isVariablesValid = $event"
/>
</InputField>
<Button type="submit" :text="$t('repo.deploy_pipeline.trigger')" :disabled="!isFormValid" />
/> </InputField
> <Button type="submit" :text="$t('repo.deploy_pipeline.trigger')" :disabled="!isFormValid" />
</form>
</Panel>
</Popup>
</Panel
> </Popup
>
</template>
<script lang="ts" setup>

View File

@ -1,28 +1,30 @@
<template>
<header
class="border-wp-background-400 bg-wp-background-100 text-wp-text-100 shadow-sm dark:border-wp-background-100 dark:bg-wp-background-300"
:class="{ 'md:px-4': fullWidth }"
>
<Container :full-width="fullWidth" class="!py-0">
<Container :full-width="fullWidth" class="!py-0"
>
<div class="flex w-full flex-col gap-2 py-2 md:flex-row md:items-center md:justify-between md:gap-10">
<div
class="flex min-h-10 content-start items-center"
:class="{
'md:flex-1': searchBoxPresent,
}"
>
<IconButton
<IconButton
v-if="goBack"
icon="back"
:title="$t('back')"
class="md:display-unset mr-2 hidden h-8 w-8 flex-shrink-0 md:justify-between"
@click="goBack"
/>
<h1 class="flex min-w-0 items-center gap-x-2 text-xl italic text-wp-text-100">
<slot name="title" />
</h1>
<h1 class="flex min-w-0 items-center gap-x-2 text-xl italic text-wp-text-100"> <slot name="title" /> </h1>
</div>
<TextField
<TextField
v-if="searchBoxPresent"
class="order-3 w-full flex-grow md:order-none md:w-auto"
:aria-label="$t('search')"
@ -37,18 +39,22 @@
'md:flex-1': searchBoxPresent,
}"
>
<slot name="headerActions" />
<slot name="headerActions" />
</div>
</div>
<div v-if="enableTabs" class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between md:py-0">
<Tabs class="order-2 md:order-none" />
<Tabs class="order-2 md:order-none" />
<div v-if="$slots.headerActions" class="flex flex-wrap content-start pb-2 md:justify-end">
<slot name="tabActions" />
<slot name="tabActions" />
</div>
</div>
</Container>
</Container
>
</header>
</template>
<script setup lang="ts">

View File

@ -1,20 +1,14 @@
<template>
<Header
<Header
:go-back="goBack"
:enable-tabs="enableTabs"
:search="search"
:full-width="fullWidthHeader"
@update:search="(value) => $emit('update:search', value)"
>
<template #title><slot name="title" /></template>
<template v-if="$slots.headerActions" #headerActions><slot name="headerActions" /></template>
<template v-if="$slots.tabActions" #tabActions><slot name="tabActions" /></template>
</Header>
<slot v-if="fluidContent" />
<Container v-else>
<slot />
</Container>
> <template #title><slot name="title" /></template> <template v-if="$slots.headerActions" #headerActions
><slot name="headerActions" /></template
> <template v-if="$slots.tabActions" #tabActions><slot name="tabActions" /></template> </Header
> <slot v-if="fluidContent" /> <Container v-else> <slot /> </Container>
</template>
<script setup lang="ts">

View File

@ -1,49 +1,48 @@
<template>
<!-- Main tabs container -->
<!-- Main tabs container -->
<div ref="tabsRef" class="flex min-w-0 flex-auto gap-4">
<router-link
<router-link
v-for="tab in visibleTabs"
:key="tab.title"
:to="tab.to"
class="flex cursor-pointer items-center whitespace-nowrap border-b-2 border-transparent py-1 text-wp-text-100"
:active-class="tab.matchChildren ? '!border-wp-custom-highlight-100' : ''"
:exact-active-class="tab.matchChildren ? '' : '!border-wp-custom-highlight-100'"
>
<span
> <span
class="flex w-full min-w-20 flex-row items-center justify-center gap-2 rounded-md px-2 py-1 hover:bg-wp-background-200 dark:hover:bg-wp-background-100"
>
<Icon v-if="tab.icon" :name="tab.icon" :class="tab.iconClass" class="flex-shrink-0" />
<span>{{ tab.title }}</span>
<CountBadge v-if="tab.count" :value="tab.count" />
</span>
</router-link>
<!-- Overflow dropdown -->
> <Icon v-if="tab.icon" :name="tab.icon" :class="tab.iconClass" class="flex-shrink-0" /> <span>{{
tab.title
}}</span
> <CountBadge v-if="tab.count" :value="tab.count" /> </span
> </router-link
> <!-- Overflow dropdown -->
<div v-if="hiddenTabs.length" class="relative border-b-2 border-transparent py-1">
<IconButton icon="dots" class="tabs-more-button h-8 w-8" @click="toggleDropdown" />
<IconButton icon="dots" class="tabs-more-button h-8 w-8" @click="toggleDropdown" />
<div
v-if="isDropdownOpen"
class="tabs-dropdown absolute z-20 mt-1 rounded-md border border-wp-background-400 bg-wp-background-100 shadow-lg dark:bg-wp-background-200"
:class="[visibleTabs.length === 0 ? 'left-0' : 'right-0']"
>
<router-link
<router-link
v-for="tab in hiddenTabs"
:key="tab.title"
:to="tab.to"
class="block w-full whitespace-nowrap p-1 text-left"
@click="isDropdownOpen = false"
>
<span
> <span
class="flex w-full min-w-20 flex-row items-center justify-center gap-2 rounded-md px-2 py-1 hover:bg-wp-background-200 dark:hover:bg-wp-background-100"
>
<Icon v-if="tab.icon" :name="tab.icon" :class="tab.iconClass" class="flex-shrink-0" />
<span>{{ tab.title }}</span>
</span>
</router-link>
> <Icon v-if="tab.icon" :name="tab.icon" :class="tab.iconClass" class="flex-shrink-0" /> <span>{{
tab.title
}}</span
> </span
> </router-link
>
</div>
</div>
</div>
</template>
<script setup lang="ts">

View File

@ -1,32 +1,38 @@
<template>
<div v-if="pipeline" class="flex w-full text-wp-text-100">
<PipelineStatusIcon :status="pipeline.status" class="flex items-center" />
<PipelineStatusIcon :status="pipeline.status" class="flex items-center" />
<div class="ml-4 flex min-w-0 flex-col">
<router-link
<router-link
:to="{
name: 'repo',
params: { repoId: pipeline.repo_id },
}"
class="underline decoration-wp-custom-highlight-100"
>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
{{ repo?.owner }} / {{ repo?.name }}
</router-link>
<span class="overflow-hidden overflow-ellipsis whitespace-nowrap italic" :title="message">{{
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> {{ repo?.owner }} / {{ repo?.name }}
</router-link
> <span class="overflow-hidden overflow-ellipsis whitespace-nowrap italic" :title="message">{{
shortMessage
}}</span>
}}</span
>
<div class="mt-2 flex flex-col">
<div class="mb-1 flex items-center space-x-2" :title="created">
<Icon name="since" />
<span>{{ since }}</span>
<Icon name="since" /> <span>{{ since }}</span
>
</div>
<div class="mb-1 flex items-center space-x-2">
<Icon name="duration" />
<span>{{ duration }}</span>
<Icon name="duration" /> <span>{{ duration }}</span
>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,11 +1,12 @@
<template>
<aside
v-if="isOpen"
ref="target"
class="z-50 flex flex-col items-center overflow-y-auto border-wp-background-400 bg-wp-background-100 dark:bg-wp-background-200"
:aria-label="$t('pipeline_feed')"
>
<router-link
<router-link
v-for="pipeline in sortedPipelines"
:key="pipeline.id"
:to="{
@ -13,12 +14,13 @@
params: { repoId: pipeline.repo_id, pipelineId: pipeline.number },
}"
class="flex w-full border-b border-wp-background-400 px-2 py-4 hover:bg-wp-background-300 hover:shadow-sm dark:hover:bg-wp-background-400"
> <PipelineFeedItem :pipeline="pipeline" /> </router-link
> <span v-if="sortedPipelines.length === 0" class="m-4 text-wp-text-100">{{
$t('repo.pipeline.no_pipelines')
}}</span
>
<PipelineFeedItem :pipeline="pipeline" />
</router-link>
<span v-if="sortedPipelines.length === 0" class="m-4 text-wp-text-100">{{ $t('repo.pipeline.no_pipelines') }}</span>
</aside>
</template>
<script lang="ts" setup>

View File

@ -1,42 +1,41 @@
<template>
<div v-if="innerValue" class="space-y-4">
<form @submit.prevent="save">
<InputField v-slot="{ id }" :label="$t('registries.address.address')">
<!-- TODO: check input field Address is a valid address -->
<TextField
<InputField v-slot="{ id }" :label="$t('registries.address.address')"
> <!-- TODO: check input field Address is a valid address --> <TextField
:id="id"
v-model="innerValue.address"
:placeholder="$t('registries.address.desc')"
required
:disabled="isEditing || isReadOnly"
/>
</InputField>
<InputField v-slot="{ id }" :label="$t('username')">
<TextField
/> </InputField
> <InputField v-slot="{ id }" :label="$t('username')"
> <TextField
:id="id"
v-model="innerValue.username"
:placeholder="$t('username')"
required
:disabled="isReadOnly"
/>
</InputField>
<InputField v-if="!isReadOnly" v-slot="{ id }" :label="$t('password')">
<TextField :id="id" v-model="innerValue.password" :placeholder="$t('password')" :required="!isEditing" />
</InputField>
/> </InputField
> <InputField v-if="!isReadOnly" v-slot="{ id }" :label="$t('password')"
> <TextField :id="id" v-model="innerValue.password" :placeholder="$t('password')" :required="!isEditing" />
</InputField
>
<div v-if="!isReadOnly" class="flex gap-2">
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" />
<Button
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" /> <Button
type="submit"
color="default"
:is-loading="isSaving"
:text="isEditing ? $t('registries.save') : $t('registries.add')"
/>
</div>
</form>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,29 +1,29 @@
<template>
<div class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="registry in registries"
:key="registry.id"
class="items-center !bg-wp-background-200 shadow-md dark:!bg-wp-background-100"
>
<span>{{ registry.address }}</span>
<IconButton
> <span>{{ registry.address }}</span
> <IconButton
:icon="registry.readonly ? 'chevron-right' : 'edit'"
class="ml-auto h-8 w-8"
:title="registry.readonly ? $t('registries.view') : $t('registries.edit')"
@click="editRegistry(registry)"
/>
<IconButton
/> <IconButton
v-if="!registry.readonly"
icon="trash"
class="h-8 w-8 hover:text-wp-error-100"
:is-loading="isDeleting"
:title="$t('registries.delete')"
@click="deleteRegistry(registry)"
/>
</ListItem>
/> </ListItem
>
<div v-if="registries?.length === 0" class="ml-2">{{ $t('registries.none') }}</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,45 +1,57 @@
<template>
<router-link
<router-link
v-if="repo"
:to="{ name: 'repo', params: { repoId: repo.id } }"
class="flex cursor-pointer flex-col overflow-hidden rounded-md border border-wp-background-400 bg-wp-background-100 p-3 shadow-sm hover:bg-wp-background-300 hover:shadow-sm dark:bg-wp-background-200 dark:hover:bg-wp-background-300"
>
>
<div class="grid grid-cols-[auto,1fr] items-center gap-y-4">
<div class="text-lg text-wp-text-100">{{ `${repo.owner} / ${repo.name}` }}</div>
<div class="ml-auto text-wp-text-100">
<div
v-if="repo.visibility === RepoVisibility.Private"
:title="`${$t('repo.visibility.visibility')}: ${$t(`repo.visibility.private.private`)}`"
>
<Icon name="visibility-private" />
<Icon name="visibility-private" />
</div>
<div
v-else-if="repo.visibility === RepoVisibility.Internal"
:title="`${$t('repo.visibility.visibility')}: ${$t(`repo.visibility.internal.internal`)}`"
>
<Icon name="visibility-internal" />
<Icon name="visibility-internal" />
</div>
</div>
<div class="col-span-2 flex w-full gap-x-4 text-wp-text-100">
<template v-if="lastPipeline">
<template v-if="lastPipeline"
>
<div class="flex min-w-0 flex-1 items-center gap-x-1">
<PipelineStatusIcon v-if="lastPipeline" :status="lastPipeline.status" />
<span class="overflow-hidden overflow-ellipsis whitespace-nowrap pl-1">{{ shortMessage }}</span>
<PipelineStatusIcon v-if="lastPipeline" :status="lastPipeline.status" /> <span
class="overflow-hidden overflow-ellipsis whitespace-nowrap pl-1"
>{{ shortMessage }}</span
>
</div>
<div class="ml-auto flex flex-shrink-0 items-center gap-x-1">
<Icon name="since" />
<span>{{ since }}</span>
<Icon name="since" /> <span>{{ since }}</span
>
</div>
</template>
</template
>
<div v-else class="flex gap-x-2">
<span>{{ $t('repo.pipeline.no_pipelines') }}</span>
<span>{{ $t('repo.pipeline.no_pipelines') }}</span
>
</div>
</div>
</div>
</router-link>
</router-link
>
</template>
<script lang="ts" setup>

View File

@ -1,63 +1,81 @@
<template>
<ListItem v-if="pipeline" class="w-full !p-0">
<ListItem v-if="pipeline" class="w-full !p-0"
>
<div class="flex w-11 flex-shrink-0 items-center">
<div class="flex h-full w-6 flex-wrap items-center justify-between">
<PipelineStatusIcon class="mx-2 md:mx-3" :status="pipeline.status" />
<PipelineStatusIcon class="mx-2 md:mx-3" :status="pipeline.status" />
</div>
</div>
<div class="flex min-w-0 flex-grow flex-wrap px-4 py-0.5 !pl-0.5 md:flex-nowrap">
<div class="hidden flex-shrink-0 items-center md:flex">
<Icon v-if="pipeline.event === 'cron'" name="stopwatch" class="text-wp-text-100" />
<img v-else class="w-6 rounded-md" :src="pipeline.author_avatar" />
<Icon v-if="pipeline.event === 'cron'" name="stopwatch" class="text-wp-text-100" /> <img
v-else
class="w-6 rounded-md"
:src="pipeline.author_avatar"
/>
</div>
<div class="flex w-full min-w-0 items-center md:mx-4 md:w-auto">
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span class="md:display-unset hidden text-wp-text-alt-100">#{{ pipeline.number }}</span>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span class="md:display-unset mx-2 hidden text-wp-text-alt-100">-</span>
<span
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span
class="md:display-unset hidden text-wp-text-alt-100"
>#{{ pipeline.number }}</span
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span
class="md:display-unset mx-2 hidden text-wp-text-alt-100"
>-</span
> <span
class="overflow-hidden overflow-ellipsis whitespace-nowrap text-wp-text-100 underline md:no-underline"
:title="message"
> {{ shortMessage }} </span
>
{{ shortMessage }}
</span>
</div>
<div
class="grid w-full flex-shrink-0 grid-flow-col grid-cols-2 grid-rows-2 gap-x-4 gap-y-2 py-2 text-wp-text-100 md:ml-auto md:w-80"
>
<div class="flex min-w-0 items-center space-x-2">
<span :title="pipelineEventTitle">
<Icon v-if="pipeline.event === 'pull_request'" name="pull-request" class="" />
<Icon v-else-if="pipeline.event === 'pull_request_closed'" name="pull-request-closed" class="" />
<Icon v-else-if="pipeline.event === 'deployment'" name="deployment" class="" />
<Icon v-else-if="pipeline.event === 'tag' || pipeline.event === 'release'" name="tag" class="" />
<Icon v-else-if="pipeline.event === 'cron'" name="push" class="" />
<Icon v-else-if="pipeline.event === 'manual'" name="manual-pipeline" class="" />
<Icon v-else name="push" class="" />
</span>
<span class="truncate">{{ prettyRef }}</span>
<span :title="pipelineEventTitle"
> <Icon v-if="pipeline.event === 'pull_request'" name="pull-request" class="" /> <Icon
v-else-if="pipeline.event === 'pull_request_closed'"
name="pull-request-closed"
class=""
/> <Icon v-else-if="pipeline.event === 'deployment'" name="deployment" class="" /> <Icon
v-else-if="pipeline.event === 'tag' || pipeline.event === 'release'"
name="tag"
class=""
/> <Icon v-else-if="pipeline.event === 'cron'" name="push" class="" /> <Icon
v-else-if="pipeline.event === 'manual'"
name="manual-pipeline"
class=""
/> <Icon v-else name="push" class="" /> </span
> <span class="truncate">{{ prettyRef }}</span
>
</div>
<div class="flex min-w-0 items-center space-x-2">
<Icon name="commit" class="" />
<span class="truncate">{{ pipeline.commit.slice(0, 10) }}</span>
<Icon name="commit" class="" /> <span class="truncate">{{ pipeline.commit.slice(0, 10) }}</span
>
</div>
<div class="flex min-w-0 items-center space-x-2" :title="$t('repo.pipeline.duration')">
<Icon name="duration" />
<span class="truncate">{{ duration }}</span>
<Icon name="duration" /> <span class="truncate">{{ duration }}</span
>
</div>
<div class="flex min-w-0 items-center space-x-2" :title="$t('repo.pipeline.created', { created })">
<Icon name="since" />
<span class="truncate">{{ since }}</span>
<Icon name="since" /> <span class="truncate">{{ since }}</span
>
</div>
</div>
</div>
</ListItem>
</ListItem
>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,7 @@
<template>
<div v-if="pipelines" class="space-y-4">
<PipelineItem
<PipelineItem
v-for="pipeline in pipelines"
:key="pipeline.id"
:to="{
@ -8,11 +9,12 @@
params: { pipelineId: pipeline.number },
}"
:pipeline="pipeline"
/>
<Panel v-if="pipelines.length === 0">
<span class="text-wp-text-100">{{ $t('repo.pipeline.no_pipelines') }}</span>
</Panel>
/> <Panel v-if="pipelines.length === 0"
> <span class="text-wp-text-100">{{ $t('repo.pipeline.no_pipelines') }}</span
> </Panel
>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,35 +1,36 @@
<template>
<div v-if="pipeline" class="flex flex-col pt-8 md:pt-0">
<div
class="code-box-log flex flex-grow flex-col overflow-hidden !p-0 shadow-md md:mt-0 md:!rounded-lg"
@mouseover="showActions = true"
@mouseleave="showActions = false"
>
<div
class="fixed left-0 top-0 flex w-full flex-row items-center bg-wp-code-100 px-4 py-1 md:relative md:left-auto md:top-auto"
>
<span class="text-base font-bold text-wp-code-text-alt-100">
<span class="md:display-unset hidden">{{ $t('repo.pipeline.log_title') }}</span>
<span class="md:hidden">{{ step?.name }}</span>
</span>
<span class="text-base font-bold text-wp-code-text-alt-100"
> <span class="md:display-unset hidden">{{ $t('repo.pipeline.log_title') }}</span
> <span class="md:hidden">{{ step?.name }}</span
> </span
>
<div class="ml-auto flex flex-row items-center gap-x-2">
<IconButton
<IconButton
v-if="step?.finished !== undefined && hasLogs"
:is-loading="downloadInProgress"
:title="$t('repo.pipeline.actions.log_download')"
class="text-wp-code-text-alt-100 hover:!bg-white hover:!bg-opacity-10"
icon="download"
@click="download"
/>
<IconButton
/> <IconButton
v-if="step?.finished !== undefined && hasLogs && hasPushPermission"
:title="$t('repo.pipeline.actions.log_delete')"
class="text-wp-code-text-alt-100 hover:!bg-white hover:!bg-opacity-10"
icon="trash"
@click="deleteLogs"
/>
<IconButton
/> <IconButton
v-if="step?.finished === undefined"
:title="
autoScroll ? $t('repo.pipeline.actions.log_auto_scroll_off') : $t('repo.pipeline.actions.log_auto_scroll')
@ -37,13 +38,13 @@
class="text-wp-code-text-alt-100 hover:!bg-white hover:!bg-opacity-10"
:icon="autoScroll ? 'auto-scroll' : 'auto-scroll-off'"
@click="autoScroll = !autoScroll"
/>
<IconButton
/> <IconButton
class="text-wp-code-text-alt-100 hover:!bg-white hover:!bg-opacity-10 md:!hidden"
icon="close"
@click="$emit('update:step-id', null)"
/>
</div>
</div>
<div
@ -51,8 +52,9 @@
ref="consoleElement"
class="grid w-full max-w-full flex-grow auto-rows-min grid-cols-[min-content,minmax(0,1fr),min-content] overflow-y-auto overflow-x-hidden p-4 text-xs md:text-sm"
>
<div v-for="line in log" :key="line.index" class="contents font-mono">
<a
<a
:id="`L${line.number}`"
:href="`#L${line.number}`"
class="select-none whitespace-nowrap pl-2 pr-6 text-right text-wp-code-text-line-numbers-100"
@ -62,11 +64,8 @@
'bg-blue-600 bg-opacity-30': isSelected(line),
underline: isSelected(line),
}"
>
{{ line.number }}
</a>
<!-- eslint-disable vue/no-v-html -->
<span
> {{ line.number }} </a
> <!-- eslint-disable vue/no-v-html --> <span
class="whitespace-pre-wrap break-words align-top"
:class="{
'bg-10.168.64.121-600 bg-opacity-40 dark:bg-red-800 dark:bg-opacity-50': line.type === 'error',
@ -74,38 +73,44 @@
'bg-blue-600 bg-opacity-30': isSelected(line),
}"
v-html="line.text"
/>
<!-- eslint-enable vue/no-v-html -->
<span
/> <!-- eslint-enable vue/no-v-html --> <span
class="select-none whitespace-nowrap pr-1 text-right text-wp-code-text-line-numbers-100"
:class="{
'bg-red-600 bg-opacity-40 dark:bg-red-800 dark:bg-opacity-50': line.type === 'error',
'bg-yellow-600 bg-opacity-40 dark:bg-yellow-800 dark:bg-opacity-50': line.type === 'warning',
'bg-blue-600 bg-opacity-30': isSelected(line),
}"
> {{ formatTime(line.time) }} </span
>
{{ formatTime(line.time) }}
</span>
</div>
</div>
<div class="m-auto text-xl text-wp-text-alt-100">
<span v-if="step?.state === 'skipped'">{{ $t('repo.pipeline.actions.canceled') }}</span>
<span v-else-if="!step?.started">{{ $t('repo.pipeline.step_not_started') }}</span>
<span v-if="step?.state === 'skipped'">{{ $t('repo.pipeline.actions.canceled') }}</span
> <span v-else-if="!step?.started">{{ $t('repo.pipeline.step_not_started') }}</span
>
<div v-else-if="!loadedLogs">{{ $t('repo.pipeline.loading') }}</div>
<div v-else-if="log?.length === 0">{{ $t('repo.pipeline.no_logs') }}</div>
</div>
<div
v-if="step?.finished !== undefined"
class="text-md flex w-full items-center bg-wp-code-100 px-4 py-1 font-bold text-wp-code-text-alt-100 md:min-h-[27px]"
>
<PipelineStatusIcon :status="step.state" class="!h-4 !w-4" />
<span v-if="step?.error" class="px-2">{{ step.error }}</span>
<span v-else class="px-2">{{ $t('repo.pipeline.exit_code', { exitCode: step.exit_code }) }}</span>
<PipelineStatusIcon :status="step.state" class="!h-4 !w-4" /> <span v-if="step?.error" class="px-2">{{
step.error
}}</span
> <span v-else class="px-2">{{ $t('repo.pipeline.exit_code', { exitCode: step.exit_code }) }}</span
>
</div>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,4 @@
<template>
<CrowIcon class="crow h-15" />
</template>
<template> <CrowIcon class="crow h-15" /> </template>
<script lang="ts" setup>
import CrowIcon from '~/assets/crow.svg?component';

View File

@ -1,9 +1,10 @@
<template>
<div
class="flex items-center justify-center"
:title="$t('repo.pipeline.status.status', { status: statusDescriptions[status] })"
>
<Icon
<Icon
:name="service ? 'settings' : `status-${status}`"
size="1.5rem"
:class="{
@ -16,6 +17,7 @@
}"
/>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,6 @@
<template>
<span v-if="started" class="ml-auto text-sm">{{ duration }}</span>
<span v-if="started" class="ml-auto text-sm">{{ duration }}</span
>
</template>
<script lang="ts" setup>

View File

@ -1,98 +1,111 @@
<template>
<div class="md:min-w-xs flex w-full flex-col gap-2 pb-2 text-wp-text-100 md:w-3/12 md:max-w-md">
<div
class="flex flex-shrink-0 flex-wrap justify-between gap-1 rounded-md border border-wp-background-400 bg-wp-background-100 p-4 shadow-sm dark:bg-wp-background-200"
>
<div class="flex flex-shrink-0 items-center space-x-1">
<div class="flex items-center">
<Icon v-if="pipeline.event === 'cron'" name="stopwatch" />
<img v-else class="w-6 rounded-md" :src="pipeline.author_avatar" />
<Icon v-if="pipeline.event === 'cron'" name="stopwatch" /> <img
v-else
class="w-6 rounded-md"
:src="pipeline.author_avatar"
/>
</div>
<span>{{ pipeline.author }}</span>
<span>{{ pipeline.author }}</span
>
</div>
<a
<a
v-if="pipeline.event === 'pull_request' || pipeline.event === 'pull_request_closed'"
class="flex min-w-0 items-center space-x-1 text-wp-link-100 hover:text-wp-link-200"
:href="pipeline.forge_url"
>
<Icon name="pull-request" />
<span class="truncate">{{ prettyRef }}</span>
</a>
<router-link
> <Icon name="pull-request" /> <span class="truncate">{{ prettyRef }}</span
> </a
> <router-link
v-else-if="pipeline.event === 'push' || pipeline.event === 'manual' || pipeline.event === 'deployment'"
class="flex min-w-0 items-center space-x-1 text-wp-link-100 hover:text-wp-link-200"
:to="{ name: 'repo-branch', params: { branch: prettyRef } }"
> <Icon v-if="pipeline.event === 'manual'" name="manual-pipeline" /> <Icon
v-else-if="pipeline.event === 'push'"
name="push"
/> <Icon v-else-if="pipeline.event === 'deployment'" name="deployment" /> <span class="truncate">{{
prettyRef
}}</span
> </router-link
>
<Icon v-if="pipeline.event === 'manual'" name="manual-pipeline" />
<Icon v-else-if="pipeline.event === 'push'" name="push" />
<Icon v-else-if="pipeline.event === 'deployment'" name="deployment" />
<span class="truncate">{{ prettyRef }}</span>
</router-link>
<div v-else class="flex min-w-0 items-center space-x-1">
<Icon v-if="pipeline.event === 'tag' || pipeline.event === 'release'" name="tag" />
<span class="truncate">{{ prettyRef }}</span>
<Icon v-if="pipeline.event === 'tag' || pipeline.event === 'release'" name="tag" /> <span class="truncate">{{
prettyRef
}}</span
>
</div>
<div class="flex flex-shrink-0 items-center">
<template v-if="pipeline.event === 'pull_request'">
<Icon name="commit" />
<span>{{ pipeline.commit.slice(0, 10) }}</span>
</template>
<a
<template v-if="pipeline.event === 'pull_request'"
> <Icon name="commit" /> <span>{{ pipeline.commit.slice(0, 10) }}</span
> </template
> <a
v-else
class="flex items-center text-wp-link-100 hover:text-wp-link-200"
:href="pipeline.forge_url"
target="_blank"
> <Icon name="commit" /> <span>{{ pipeline.commit.slice(0, 10) }}</span
> </a
>
<Icon name="commit" />
<span>{{ pipeline.commit.slice(0, 10) }}</span>
</a>
</div>
</div>
<Panel v-if="pipeline.workflows === undefined || pipeline.workflows.length === 0">
<span>{{ $t('repo.pipeline.no_pipeline_steps') }}</span>
</Panel>
<Panel v-if="pipeline.workflows === undefined || pipeline.workflows.length === 0"
> <span>{{ $t('repo.pipeline.no_pipeline_steps') }}</span
> </Panel
>
<div class="relative min-h-0 w-full flex-grow">
<div class="absolute left-0 right-0 top-0 flex h-full flex-col gap-y-2 md:overflow-y-auto">
<div
v-for="workflow in pipeline.workflows"
:key="workflow.id"
class="rounded-md border border-wp-background-400 bg-wp-background-100 p-2 shadow-sm dark:bg-wp-background-200"
>
<div class="flex flex-col gap-2">
<div v-if="workflow.environ" class="flex flex-wrap justify-end gap-x-1 gap-y-2 pr-1 pt-1 text-xs">
<div v-for="(value, key) in workflow.environ" :key="key">
<Badge :label="key" :value="value" />
</div>
<div v-for="(value, key) in workflow.environ" :key="key"> <Badge :label="key" :value="value" /> </div>
</div>
<button
<button
v-if="!singleConfig"
type="button"
:title="workflow.name"
class="hover-effect flex items-center gap-2 rounded-md px-1 py-2 hover:bg-wp-background-300 dark:hover:bg-wp-background-400"
@click="workflowsCollapsed[workflow.id] = !workflowsCollapsed[workflow.id]"
>
<Icon
<Icon
name="chevron-right"
class="h-6 min-w-6 transition-transform duration-150"
:class="{ 'rotate-90 transform': !workflowsCollapsed[workflow.id] }"
/>
<PipelineStatusIcon :status="workflow.state" class="!h-4 !w-4" />
<span class="truncate">{{ workflow.name }}</span>
<PipelineStepDuration
/> <PipelineStatusIcon :status="workflow.state" class="!h-4 !w-4" /> <span class="truncate">{{
workflow.name
}}</span
> <PipelineStepDuration
v-if="workflow.started !== workflow.finished"
:workflow="workflow"
class="pr-2px mr-1"
/>
</button>
/> </button
>
</div>
<div
class="overflow-hidden transition-height duration-150"
:class="{ 'max-h-0': workflowsCollapsed[workflow.id], 'ml-[1.6rem]': !singleConfig }"
>
<button
<button
v-for="step in workflow.children"
:key="step.pid"
type="button"
@ -104,15 +117,20 @@
}"
@click="$emit('update:selected-step-id', step.pid)"
>
<PipelineStatusIcon :service="step.type === StepType.Service" :status="step.state" class="!h-4 !w-4" />
<span class="truncate">{{ step.name }}</span>
<PipelineStepDuration :step="step" />
</button>
<PipelineStatusIcon :service="step.type === StepType.Service" :status="step.state" class="!h-4 !w-4" />
<span class="truncate">{{ step.name }}</span
> <PipelineStepDuration :step="step" /> </button
>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,57 +1,68 @@
<template>
<div v-if="innerValue" class="space-y-4">
<form @submit.prevent="save">
<InputField v-slot="{ id }" :label="$t('secrets.name')">
<TextField
<InputField v-slot="{ id }" :label="$t('secrets.name')"
> <TextField
:id="id"
v-model="innerValue.name"
:placeholder="$t('secrets.name')"
required
:disabled="isEditingSecret"
/>
</InputField>
<InputField v-slot="{ id }" :label="$t('secrets.value')">
<TextField
/> </InputField
> <InputField v-slot="{ id }" :label="$t('secrets.value')"
> <TextField
:id="id"
v-model="innerValue.value"
:placeholder="$t('secrets.value')"
:lines="5"
:required="!isEditingSecret"
/>
</InputField>
<InputField v-slot="{ id }" :label="$t('secrets.plugins.images')">
<span class="mb-2 ml-1 text-wp-text-alt-100">{{ $t('secrets.plugins.desc') }}</span>
/> </InputField
> <InputField v-slot="{ id }" :label="$t('secrets.plugins.images')"
> <span class="mb-2 ml-1 text-wp-text-alt-100">{{ $t('secrets.plugins.desc') }}</span
>
<div class="flex flex-col gap-2">
<div v-for="image in innerValue.images" :key="image" class="flex gap-2">
<TextField :id="id" :model-value="image" disabled />
<Button type="button" color="gray" start-icon="trash" @click="removeImage(image)" />
<TextField :id="id" :model-value="image" disabled /> <Button
type="button"
color="gray"
start-icon="trash"
@click="removeImage(image)"
/>
</div>
<div class="flex gap-2">
<TextField :id="id" v-model="newImage" @keydown.enter.prevent="addNewImage" />
<Button type="button" color="gray" start-icon="plus" @click="addNewImage" />
<TextField :id="id" v-model="newImage" @keydown.enter.prevent="addNewImage" /> <Button
type="button"
color="gray"
start-icon="plus"
@click="addNewImage"
/>
</div>
</div>
</InputField>
<InputField :label="$t('secrets.events.events')">
<Warning class="mb-4 text-sm" :text="$t('secrets.events.warning')" />
<CheckboxesField v-model="innerValue.events" :options="secretEventsOptions" />
</InputField>
</InputField
> <InputField :label="$t('secrets.events.events')"
> <Warning class="mb-4 text-sm" :text="$t('secrets.events.warning')" /> <CheckboxesField
v-model="innerValue.events"
:options="secretEventsOptions"
/> </InputField
>
<div class="flex gap-2">
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" />
<Button
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" /> <Button
type="submit"
color="default"
:is-loading="isSaving"
:text="isEditingSecret ? $t('secrets.save') : $t('secrets.add')"
/>
</div>
</form>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,38 +1,38 @@
<template>
<div class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="secret in secrets"
:key="secret.id"
class="items-center !bg-wp-background-200 shadow-md dark:!bg-wp-background-100"
>
<span>{{ secret.name }}</span>
<Badge
> <span>{{ secret.name }}</span
> <Badge
v-if="secret.edit === false"
class="ml-2"
:label="secret.org_id === 0 ? $t('global_level_secret') : $t('org_level_secret')"
/>
<div class="md:display-unset ml-auto hidden space-x-2">
<Badge v-for="event in secret.events" :key="event" :label="event" />
<Badge v-for="event in secret.events" :key="event" :label="event" />
</div>
<template v-if="secret.edit !== false">
<IconButton
<template v-if="secret.edit !== false"
> <IconButton
icon="edit"
class="ml-auto h-8 w-8 md:ml-2"
:title="$t('secrets.edit')"
@click="editSecret(secret)"
/>
<IconButton
/> <IconButton
icon="trash"
class="ml-2 h-8 w-8 hover:text-wp-error-100"
:is-loading="isDeleting"
:title="$t('secrets.delete')"
@click="deleteSecret(secret)"
/>
</template>
</ListItem>
/> </template
> </ListItem
>
<div v-if="secrets?.length === 0" class="ml-2">{{ $t('secrets.none') }}</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,40 +1,49 @@
<template>
<main class="flex h-full w-full flex-col items-center justify-center">
<Error v-if="errorMessage" class="w-full md:w-3xl">
<span class="whitespace-pre">{{ errorMessage }}</span>
<span v-if="errorDescription" class="mt-1 whitespace-pre">{{ errorDescription }}</span>
<a
<Error v-if="errorMessage" class="w-full md:w-3xl"
> <span class="whitespace-pre">{{ errorMessage }}</span
> <span v-if="errorDescription" class="mt-1 whitespace-pre">{{ errorDescription }}</span
> <a
v-if="errorUri"
:href="errorUri"
target="_blank"
class="mt-1 cursor-pointer text-wp-link-100 hover:text-wp-link-200"
>
<span>{{ errorUri }}</span>
</a>
</Error>
> <span>{{ errorUri }}</span
> </a
> </Error
>
<div
class="flex min-h-sm w-full flex-col overflow-hidden border border-wp-background-400 bg-wp-background-100 shadow-sm dark:bg-wp-background-200 md:m-8 md:w-3xl md:flex-row md:rounded-md"
>
<div class="flex min-h-48 items-center justify-center bg-wp-background-secondary-800 dark:bg-wp-primary-300 md:w-3/5">
<CrowLogo preserveAspectRatio="xMinYMin slice" class="h-32 w-auto md:h-48" />
<div
class="flex min-h-48 items-center justify-center bg-wp-background-secondary-800 dark:bg-wp-primary-300 md:w-3/5"
>
<CrowLogo preserveAspectRatio="xMinYMin slice" class="h-32 w-auto md:h-48" />
</div>
<div class="flex min-h-48 flex-col items-center justify-center gap-4 p-4 text-center md:w-2/5">
<h1 class="text-xl text-wp-text-100">{{ $t('welcome') }}</h1>
<div class="flex flex-col gap-2">
<Button
<Button
v-for="forge in forges"
:key="forge.id"
:start-icon="forge.type === 'addon' ? 'repo' : forge.type"
class="!whitespace-normal"
@click="doLogin(forge.id)"
> {{ $t('login_with', { forge: getHostFromUrl(forge) }) }} </Button
>
{{ $t('login_with', { forge: getHostFromUrl(forge) }) }}
</Button>
</div>
</div>
</div>
</main>
</template>
<script lang="ts" setup>

View File

@ -1,8 +1,9 @@
<template>
<div class="flex h-full w-full flex-col items-center justify-center">
<p class="mb-8 text-2xl">{{ $t('not_found.not_found') }}</p>
<router-link class="text-blue-400" replace :to="{ name: 'home' }">
{{ $t('not_found.back_home') }}
</router-link>
<router-link class="text-blue-400" replace :to="{ name: 'home' }"> {{ $t('not_found.back_home') }} </router-link>
</div>
</template>

View File

@ -1,34 +1,31 @@
<template>
<Scaffold v-model:search="search" :go-back="goBack">
<template #title>
{{ $t('repo.add') }}
</template>
<Scaffold v-model:search="search" :go-back="goBack"
> <template #title> {{ $t('repo.add') }} </template>
<div class="space-y-4">
<template v-if="repos !== undefined && repos.length > 0">
<ListItem
<template v-if="repos !== undefined && repos.length > 0"
> <ListItem
v-for="repo in searchedRepos"
:key="repo.id"
class="items-center"
:to="repo.active ? { name: 'repo', params: { repoId: repo.id } } : undefined"
>
<span class="text-wp-text-100">{{ repo.full_name }}</span>
<span v-if="repo.active" class="ml-auto text-wp-text-alt-100">{{ $t('repo.enable.enabled') }}</span>
> <span class="text-wp-text-100">{{ repo.full_name }}</span
> <span v-if="repo.active" class="ml-auto text-wp-text-alt-100">{{ $t('repo.enable.enabled') }}</span
>
<div v-else class="ml-auto flex items-center">
<Badge v-if="repo.id" class="md:display-unset mr-2 hidden" :label="$t('repo.enable.disabled')" />
<Button
<Badge v-if="repo.id" class="md:display-unset mr-2 hidden" :label="$t('repo.enable.disabled')" /> <Button
:text="$t('repo.enable.enable')"
:is-loading="isActivatingRepo && repoToActivate?.forge_remote_id === repo.forge_remote_id"
@click="activateRepo(repo)"
/>
</div>
</ListItem>
</template>
<div v-else-if="loading" class="flex justify-center text-wp-text-100">
<Icon name="spinner" />
</div>
</ListItem
> </template
>
<div v-else-if="loading" class="flex justify-center text-wp-text-100"> <Icon name="spinner" /> </div>
</div>
</Scaffold>
</Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,37 +1,41 @@
<template>
<Scaffold v-model:search="search">
<template #title>
{{ $t('repositories') }}
</template>
<template #headerActions>
<Button :to="{ name: 'repo-add' }" start-icon="plus" :text="$t('repo.add')" />
</template>
<Transition name="fade" mode="out-in">
<Scaffold v-model:search="search"
> <template #title> {{ $t('repositories') }} </template> <template #headerActions
> <Button :to="{ name: 'repo-add' }" start-icon="plus" :text="$t('repo.add')" /> </template
> <Transition name="fade" mode="out-in"
>
<div v-if="search === '' && repos.length > 0" class="grid gap-8">
<div
v-if="reposLastAccess.length > 0 && repos.length > 4"
class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2"
>
<RepoItem v-for="repo in reposLastAccess" :key="repo.id" :repo="repo" />
<RepoItem v-for="repo in reposLastAccess" :key="repo.id" :repo="repo" />
</div>
<div class="flex flex-col gap-4">
<h2 class="text-lg text-wp-text-100">{{ $t('all_repositories') }}</h2>
<div class="flex flex-col gap-4">
<RepoItem v-for="repo in reposLastActivity" :key="repo.id" :repo="repo" />
<RepoItem v-for="repo in reposLastActivity" :key="repo.id" :repo="repo" />
</div>
</div>
</div>
<div v-else class="flex flex-col">
<div v-if="reposLastActivity.length > 0" class="flex flex-col gap-4">
<RepoItem v-for="repo in reposLastActivity" :key="repo.id" :repo="repo" />
<RepoItem v-for="repo in reposLastActivity" :key="repo.id" :repo="repo" />
</div>
<span v-else class="text-center text-lg text-wp-text-100">{{ $t('no_search_results') }}</span>
<span v-else class="text-center text-lg text-wp-text-100">{{ $t('no_search_results') }}</span
>
</div>
</Transition>
</Scaffold>
</Transition
> </Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,3 +1 @@
<template>
<router-view />
</template>
<template> <router-view /> </template>

View File

@ -1,5 +1,5 @@
<template>
<AgentManager
<AgentManager
:description="$t('admin.settings.agents.desc')"
:load-agents="loadAgents"
:create-agent="createAgent"

View File

@ -1,32 +1,29 @@
<template>
<Settings :title="$t('info')">
<Settings :title="$t('info')"
>
<div class="flex flex-col items-center gap-4">
<!-- <component :is="isLightTheme ? CrowLogoDark : CrowLogo" class="w-32 h-32 fill-wp-text-200" /> -->
<!-- FIXME: theme-switch is working but the dark svg is somewhat not rendered correctly -->
<component :is="CrowLogo" v-if="!isLightTheme" class="h-32 w-32 fill-wp-text-200" />
<i18n-t keypath="running_version" tag="p" class="text-center text-xl">
<span class="font-bold">{{ version?.current }}</span>
</i18n-t>
<Error v-if="version?.needsUpdate">
<i18n-t keypath="update_crow" tag="span">
<a
<!-- <component :is="isLightTheme ? CrowLogoDark : CrowLogo" class="w-32 h-32 fill-wp-text-200" /> --> <!-- FIXME: theme-switch is working but the dark svg is somewhat not rendered correctly -->
<component :is="CrowLogo" v-if="!isLightTheme" class="h-32 w-32 fill-wp-text-200" /> <i18n-t
keypath="running_version"
tag="p"
class="text-center text-xl"
> <span class="font-bold">{{ version?.current }}</span
> </i18n-t
> <Error v-if="version?.needsUpdate"
> <i18n-t keypath="update_crow" tag="span"
> <a
v-if="!version.usesNext"
:href="`https://codeberg.org/crowci/crow/releases/tag/${version.latest}`"
target="_blank"
rel="noopener noreferrer"
class="underline"
>
{{ version.latest }}
</a>
<span v-else>
{{ version.latest }}
</span>
</i18n-t>
</Error>
> {{ version.latest }} </a
> <span v-else> {{ version.latest }} </span> </i18n-t
> </Error
>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,36 +1,35 @@
<template>
<Settings :title="$t('admin.settings.orgs.orgs')" :description="$t('admin.settings.orgs.desc')">
<Settings :title="$t('admin.settings.orgs.orgs')" :description="$t('admin.settings.orgs.desc')"
>
<div class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="org in orgs"
:key="org.id"
class="items-center gap-2 !bg-wp-background-200 dark:!bg-wp-background-100"
>
<span>{{ org.name }}</span>
<IconButton
> <span>{{ org.name }}</span
> <IconButton
icon="chevron-right"
:title="$t('admin.settings.orgs.view')"
class="ml-auto h-8 w-8"
:to="{ name: 'org', params: { orgId: org.id } }"
/>
<IconButton
/> <IconButton
icon="settings-outline"
:title="$t('admin.settings.orgs.org_settings')"
class="h-8 w-8"
:to="{ name: 'org-settings', params: { orgId: org.id } }"
/>
<IconButton
/> <IconButton
icon="trash"
:title="$t('admin.settings.orgs.delete_org')"
class="ml-2 h-8 w-8 hover:text-wp-error-100"
:is-loading="isDeleting"
@click="deleteOrg(org)"
/>
</ListItem>
/> </ListItem
>
<div v-if="orgs?.length === 0" class="ml-2">{{ $t('admin.settings.orgs.none') }}</div>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,16 +1,16 @@
<template>
<Settings :title="$t('admin.settings.queue.queue')" :description="$t('admin.settings.queue.desc')">
<template #headerActions>
<Settings :title="$t('admin.settings.queue.queue')" :description="$t('admin.settings.queue.desc')"
> <template #headerActions
>
<div v-if="queueInfo">
<div class="flex items-center gap-2">
<Button
<Button
v-if="queueInfo.paused"
:text="$t('admin.settings.queue.resume')"
start-icon="play"
@click="resumeQueue"
/>
<Button v-else :text="$t('admin.settings.queue.pause')" start-icon="pause" @click="pauseQueue" />
<Icon
/> <Button v-else :text="$t('admin.settings.queue.pause')" start-icon="pause" @click="pauseQueue" /> <Icon
:name="queueInfo.paused ? 'pause' : 'play'"
class="h-6 w-6"
:class="{
@ -19,19 +19,20 @@
}"
/>
</div>
</div>
</template>
</template
>
<div class="flex flex-col">
<AdminQueueStats :stats="queueInfo?.stats" />
<AdminQueueStats :stats="queueInfo?.stats" />
<div v-if="tasks.length > 0" class="flex flex-col">
<p class="mb-2 mt-6 text-xl">{{ $t('admin.settings.queue.tasks') }}</p>
<ListItem
<ListItem
v-for="task in tasks"
:key="task.id"
class="mb-2 items-center !bg-wp-background-200 shadow-md dark:!bg-wp-background-100"
>
>
<div
class="flex items-center"
:title="
@ -42,7 +43,7 @@
: $t('admin.settings.queue.task_waiting_on_deps')
"
>
<Icon
<Icon
:name="
task.status === 'pending'
? 'status-pending'
@ -57,22 +58,23 @@
}"
/>
</div>
<span class="ml-2">{{ task.id }}</span>
<span class="ml-auto flex gap-2">
<Badge v-if="task.agent_id !== 0" :label="$t('admin.settings.queue.agent')" :value="task.agent_id" />
<template v-for="(value, label) in task.labels">
<Badge v-if="value" :key="label" :label="label.toString()" :value="value" />
</template>
<Badge
<span class="ml-2">{{ task.id }}</span
> <span class="ml-auto flex gap-2"
> <Badge v-if="task.agent_id !== 0" :label="$t('admin.settings.queue.agent')" :value="task.agent_id" />
<template v-for="(value, label) in task.labels"
> <Badge v-if="value" :key="label" :label="label.toString()" :value="value" /> </template
> <Badge
v-if="task.dependencies"
:label="$t('admin.settings.queue.waiting_for')"
:value="task.dependencies.join(', ')"
/>
</span>
</ListItem>
/> </span
> </ListItem
>
</div>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,39 +1,30 @@
<template>
<Settings
<Settings
:title="$t('registries.registries')"
:description="$t('admin.settings.registries.desc')"
docs-url="docs/usage/registries"
>
<template #headerActions>
<Button
> <template #headerActions
> <Button
v-if="selectedRegistry"
:text="$t('registries.show')"
start-icon="back"
@click="selectedRegistry = undefined"
/>
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
</template>
<template #headerEnd>
<Warning class="mt-4 text-sm" :text="$t('admin.settings.registries.warning')" />
</template>
/> <Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" /> </template
> <template #headerEnd> <Warning class="mt-4 text-sm" :text="$t('admin.settings.registries.warning')" /> </template>
<RegistryList
v-if="!selectedRegistry"
v-model="registries"
:is-deleting="isDeleting"
@edit="editRegistry"
@delete="deleteRegistry"
/>
<RegistryEdit
/> <RegistryEdit
v-else
v-model="selectedRegistry"
:is-saving="isSaving"
@save="createRegistry"
@cancel="selectedRegistry = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,45 +1,44 @@
<template>
<Settings :title="$t('admin.settings.repos.repos')" :description="$t('admin.settings.repos.desc')">
<template #headerActions>
<Button
<Settings :title="$t('admin.settings.repos.repos')" :description="$t('admin.settings.repos.desc')"
> <template #headerActions
> <Button
start-icon="heal"
:is-loading="isRepairingRepos"
:text="$t('admin.settings.repos.repair.repair')"
@click="repairRepos"
/>
</template>
/> </template
>
<div class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="repo in repos"
:key="repo.id"
class="items-center gap-2 bg-wp-background-200 dark:!bg-wp-background-100"
>
<span>{{ repo.full_name }}</span>
> <span>{{ repo.full_name }}</span
>
<div class="ml-auto flex items-center">
<Badge
<Badge
v-if="!repo.active"
class="md:display-unset mr-2 hidden"
:label="$t('admin.settings.repos.disabled')"
/>
<IconButton
/> <IconButton
icon="chevron-right"
:title="$t('admin.settings.repos.view')"
class="h-8 w-8"
:to="{ name: 'repo', params: { repoId: repo.id } }"
/>
<IconButton
/> <IconButton
icon="settings-outline"
:title="$t('admin.settings.repos.settings')"
class="h-8 w-8"
:to="{ name: 'repo-settings', params: { repoId: repo.id } }"
/>
</div>
</ListItem>
</ListItem
>
<div v-if="repos?.length === 0" class="ml-2">{{ $t('admin.settings.repos.none') }}</div>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,34 +1,30 @@
<template>
<Settings
<Settings
:title="$t('secrets.secrets')"
:description="$t('admin.settings.secrets.desc')"
docs-url="docs/usage/secrets"
>
<template #headerActions>
<Button v-if="selectedSecret" :text="$t('secrets.show')" start-icon="back" @click="selectedSecret = undefined" />
<Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" />
</template>
<template #headerEnd>
<Warning class="mt-4 text-sm" :text="$t('admin.settings.secrets.warning')" />
</template>
> <template #headerActions
> <Button
v-if="selectedSecret"
:text="$t('secrets.show')"
start-icon="back"
@click="selectedSecret = undefined"
/> <Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" /> </template
> <template #headerEnd> <Warning class="mt-4 text-sm" :text="$t('admin.settings.secrets.warning')" /> </template>
<SecretList
v-if="!selectedSecret"
v-model="secrets"
:is-deleting="isDeleting"
@edit="editSecret"
@delete="deleteSecret"
/>
<SecretEdit
/> <SecretEdit
v-else
v-model="selectedSecret"
:is-saving="isSaving"
@save="createSecret"
@cancel="selectedSecret = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,19 +1,18 @@
<template>
<Scaffold enable-tabs>
<template #title>
{{ $t('settings') }}
</template>
<Tab :to="{ name: 'admin-settings' }" :title="$t('info')" />
<Tab :to="{ name: 'admin-settings-secrets' }" :title="$t('secrets.secrets')" />
<Tab :to="{ name: 'admin-settings-registries' }" :title="$t('registries.registries')" />
<Tab :to="{ name: 'admin-settings-repos' }" :title="$t('admin.settings.repos.repos')" />
<Tab :to="{ name: 'admin-settings-users' }" :title="$t('admin.settings.users.users')" />
<Tab :to="{ name: 'admin-settings-orgs' }" :title="$t('admin.settings.orgs.orgs')" />
<Tab :to="{ name: 'admin-settings-agents' }" :title="$t('admin.settings.agents.agents')" />
<Tab :to="{ name: 'admin-settings-queue' }" :title="$t('admin.settings.queue.queue')" />
<router-view />
</Scaffold>
<Scaffold enable-tabs
> <template #title> {{ $t('settings') }} </template> <Tab :to="{ name: 'admin-settings' }" :title="$t('info')" />
<Tab :to="{ name: 'admin-settings-secrets' }" :title="$t('secrets.secrets')" /> <Tab
:to="{ name: 'admin-settings-registries' }"
:title="$t('registries.registries')"
/> <Tab :to="{ name: 'admin-settings-repos' }" :title="$t('admin.settings.repos.repos')" /> <Tab
:to="{ name: 'admin-settings-users' }"
:title="$t('admin.settings.users.users')"
/> <Tab :to="{ name: 'admin-settings-orgs' }" :title="$t('admin.settings.orgs.orgs')" /> <Tab
:to="{ name: 'admin-settings-agents' }"
:title="$t('admin.settings.agents.agents')"
/> <Tab :to="{ name: 'admin-settings-queue' }" :title="$t('admin.settings.queue.queue')" /> <router-view />
</Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,84 +1,78 @@
<template>
<Settings :title="$t('admin.settings.users.users')" :description="$t('admin.settings.users.desc')">
<template #headerActions>
<Button
<Settings :title="$t('admin.settings.users.users')" :description="$t('admin.settings.users.desc')"
> <template #headerActions
> <Button
v-if="selectedUser"
:text="$t('admin.settings.users.show')"
start-icon="back"
@click="selectedUser = undefined"
/>
<Button v-else :text="$t('admin.settings.users.add')" start-icon="plus" @click="showAddUser" />
</template>
/> <Button v-else :text="$t('admin.settings.users.add')" start-icon="plus" @click="showAddUser" /> </template
>
<div v-if="!selectedUser" class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="user in users"
:key="user.id"
class="items-center gap-2 !bg-wp-background-200 dark:!bg-wp-background-100"
>
<img v-if="user.avatar_url" class="h-6 rounded-md" :src="user.avatar_url" />
<span>{{ user.login }}</span>
<Badge
> <img v-if="user.avatar_url" class="h-6 rounded-md" :src="user.avatar_url" /> <span>{{ user.login }}</span
> <Badge
v-if="user.admin"
class="md:display-unset ml-auto hidden"
:label="$t('admin.settings.users.admin.admin')"
/>
<IconButton
/> <IconButton
icon="edit"
:title="$t('admin.settings.users.edit_user')"
class="md:display-unset h-8 w-8"
:class="{ 'ml-auto': !user.admin, 'ml-2': user.admin }"
@click="editUser(user)"
/>
<IconButton
/> <IconButton
icon="trash"
:title="$t('admin.settings.users.delete_user')"
class="ml-2 h-8 w-8 hover:text-wp-error-100"
:is-loading="isDeleting"
@click="deleteUser(user)"
/>
</ListItem>
/> </ListItem
>
<div v-if="users?.length === 0" class="ml-2">{{ $t('admin.settings.users.none') }}</div>
</div>
<div v-else>
<form @submit.prevent="saveUser">
<InputField v-slot="{ id }" :label="$t('admin.settings.users.login')">
<TextField :id="id" v-model="selectedUser.login" :disabled="isEditingUser" />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.users.email')">
<TextField :id="id" v-model="selectedUser.email" />
</InputField>
<InputField v-slot="{ id }" :label="$t('admin.settings.users.avatar_url')">
<InputField v-slot="{ id }" :label="$t('admin.settings.users.login')"
> <TextField :id="id" v-model="selectedUser.login" :disabled="isEditingUser" /> </InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.users.email')"
> <TextField :id="id" v-model="selectedUser.email" /> </InputField
> <InputField v-slot="{ id }" :label="$t('admin.settings.users.avatar_url')"
>
<div class="flex gap-2">
<img v-if="selectedUser.avatar_url" class="h-8 w-8 rounded-md" :src="selectedUser.avatar_url" />
<TextField :id="id" v-model="selectedUser.avatar_url" />
<img v-if="selectedUser.avatar_url" class="h-8 w-8 rounded-md" :src="selectedUser.avatar_url" /> <TextField
:id="id"
v-model="selectedUser.avatar_url"
/>
</div>
</InputField>
<InputField :label="$t('admin.settings.users.admin.admin')">
<Checkbox
</InputField
> <InputField :label="$t('admin.settings.users.admin.admin')"
> <Checkbox
:model-value="selectedUser.admin || false"
:label="$t('admin.settings.users.admin.placeholder')"
@update:model-value="selectedUser!.admin = $event"
/>
</InputField>
/> </InputField
>
<div class="flex gap-2">
<Button :text="$t('admin.settings.users.cancel')" @click="selectedUser = undefined" />
<Button
<Button :text="$t('admin.settings.users.cancel')" @click="selectedUser = undefined" /> <Button
:is-loading="isSaving"
type="submit"
color="default"
:text="isEditingUser ? $t('admin.settings.users.save') : $t('admin.settings.users.add')"
/>
</div>
</form>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,30 +1,45 @@
<template>
<div class="m-auto flex flex-col gap-4">
<div class="text-center text-wp-text-100">
<CrowLogo preserveAspectRatio="xMinYMin slice" class="m-auto mb-8 w-32" />
<template v-if="state === 'confirm'">
<CrowLogo preserveAspectRatio="xMinYMin slice" class="m-auto mb-8 w-32" /> <template v-if="state === 'confirm'"
>
<h1 class="text-4xl font-bold">{{ $t('login_to_cli') }}</h1>
<p class="text-2xl">{{ $t('login_to_cli_description') }}</p>
</template>
<template v-else-if="state === 'success'">
</template
> <template v-else-if="state === 'success'"
>
<h1 class="text-4xl font-bold">{{ $t('cli_login_success') }}</h1>
<p class="text-2xl">{{ $t('return_to_cli') }}</p>
</template>
<template v-else-if="state === 'failed'">
</template
> <template v-else-if="state === 'failed'"
>
<h1 class="mt-4 text-4xl font-bold">{{ $t('cli_login_failed') }}</h1>
<p class="text-2xl">{{ $t('return_to_cli') }}</p>
</template>
<template v-else-if="state === 'denied'">
</template
> <template v-else-if="state === 'denied'"
>
<h1 class="mt-4 text-4xl font-bold">{{ $t('cli_login_denied') }}</h1>
<p class="text-2xl">{{ $t('return_to_cli') }}</p>
</template>
</template
>
</div>
<div v-if="state === 'confirm'" class="flex justify-center gap-4">
<Button :text="$t('login_to_cli')" color="default" @click="sendToken(false)" />
<Button :text="$t('abort')" color="red" @click="abortLogin" />
<Button :text="$t('login_to_cli')" color="default" @click="sendToken(false)" /> <Button
:text="$t('abort')"
color="red"
@click="abortLogin"
/>
</div>
</div>
</template>
<script setup lang="ts">

View File

@ -1,5 +1,7 @@
<template>
<div />
</template>
<script setup lang="ts">

View File

@ -1,25 +1,21 @@
<template>
<Scaffold v-if="org && orgPermissions" v-model:search="search">
<template #title>
{{ org.name }}
</template>
<template #headerActions>
<IconButton
<Scaffold v-if="org && orgPermissions" v-model:search="search"
> <template #title> {{ org.name }} </template> <template #headerActions
> <IconButton
v-if="orgPermissions.admin"
icon="settings"
:to="{ name: org.is_user ? 'user' : 'org-settings-secrets' }"
:title="$t('settings')"
/>
</template>
/> </template
>
<div class="flex flex-col gap-4"> <RepoItem v-for="repo in reposLastActivity" :key="repo.id" :repo="repo" /> </div>
<div class="flex flex-col gap-4">
<RepoItem v-for="repo in reposLastActivity" :key="repo.id" :repo="repo" />
</div>
<div v-if="(reposLastActivity || []).length <= 0" class="text-center">
<span class="m-auto text-wp-text-100">{{ $t('repo.user_none') }}</span>
<span class="m-auto text-wp-text-100">{{ $t('repo.user_none') }}</span
>
</div>
</Scaffold>
</Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,21 +1,14 @@
<template>
<Scaffold v-if="org && orgPermissions && route.meta.orgHeader">
<template #title>
{{ org.name }}
</template>
<template #headerActions>
<IconButton
<Scaffold v-if="org && orgPermissions && route.meta.orgHeader"
> <template #title> {{ org.name }} </template> <template #headerActions
> <IconButton
v-if="orgPermissions.admin"
:to="{ name: org.is_user ? 'user' : 'org-settings-secrets' }"
:title="$t('settings')"
icon="settings"
/>
</template>
<router-view />
</Scaffold>
<router-view v-else-if="org && orgPermissions" />
/> </template
> <router-view /> </Scaffold
> <router-view v-else-if="org && orgPermissions" />
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<AgentManager
<AgentManager
:description="$t('org.settings.agents.desc')"
:load-agents="loadAgents"
:create-agent="createAgent"

View File

@ -1,35 +1,29 @@
<template>
<Settings
<Settings
:title="$t('registries.registries')"
:description="$t('org.settings.registries.desc')"
docs-url="docs/usage/registries"
>
<template #headerActions>
<Button
> <template #headerActions
> <Button
v-if="selectedRegistry"
:text="$t('registries.show')"
start-icon="back"
@click="selectedRegistry = undefined"
/>
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
</template>
<RegistryList
/> <Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" /> </template
> <RegistryList
v-if="!selectedRegistry"
v-model="registries"
:is-deleting="isDeleting"
@edit="editRegistry"
@delete="deleteRegistry"
/>
<RegistryEdit
/> <RegistryEdit
v-else
v-model="selectedRegistry"
:is-saving="isSaving"
@save="createRegistry"
@cancel="selectedRegistry = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,26 +1,26 @@
<template>
<Settings :title="$t('secrets.secrets')" :description="$t('org.settings.secrets.desc')" docs-url="docs/usage/secrets">
<template #headerActions>
<Button v-if="selectedSecret" :text="$t('secrets.show')" start-icon="back" @click="selectedSecret = undefined" />
<Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" />
</template>
<SecretList
<Settings :title="$t('secrets.secrets')" :description="$t('org.settings.secrets.desc')" docs-url="docs/usage/secrets"
> <template #headerActions
> <Button
v-if="selectedSecret"
:text="$t('secrets.show')"
start-icon="back"
@click="selectedSecret = undefined"
/> <Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" /> </template
> <SecretList
v-if="!selectedSecret"
v-model="secrets"
:is-deleting="isDeleting"
@edit="editSecret"
@delete="deleteSecret"
/>
<SecretEdit
/> <SecretEdit
v-else
v-model="selectedSecret"
:is-saving="isSaving"
@save="createSecret"
@cancel="selectedSecret = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,26 +1,22 @@
<template>
<Scaffold v-if="org" enable-tabs :go-back="goBack">
<template #title>
<span>
<router-link :to="{ name: 'org' }" class="hover:underline hover:decoration-wp-custom-highlight-100">{{
<Scaffold v-if="org" enable-tabs :go-back="goBack"
> <template #title
> <span
> <router-link :to="{ name: 'org' }" class="hover:underline hover:decoration-wp-custom-highlight-100">{{
org.name
/* eslint-disable-next-line @intlify/vue-i18n/no-raw-text */
}}</router-link>
/
{{ $t('settings') }}
</span>
</template>
<Tab :to="{ name: 'org-settings-secrets' }" :title="$t('secrets.secrets')" />
<Tab :to="{ name: 'org-settings-registries' }" :title="$t('registries.registries')" />
<Tab
}}</router-link
> / {{ $t('settings') }} </span
> </template
> <Tab :to="{ name: 'org-settings-secrets' }" :title="$t('secrets.secrets')" /> <Tab
:to="{ name: 'org-settings-registries' }"
:title="$t('registries.registries')"
/> <Tab
v-if="useConfig().userRegisteredAgents"
:to="{ name: 'org-settings-agents' }"
:title="$t('admin.settings.agents.agents')"
/>
<router-view />
</Scaffold>
/> <router-view /> </Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,8 +1,10 @@
<template>
<div class="mb-4 flex w-full justify-center">
<span class="text-xl text-wp-text-100">{{ $t('repo.pipeline.pipelines_for', { branch }) }}</span>
<span class="text-xl text-wp-text-100">{{ $t('repo.pipeline.pipelines_for', { branch }) }}</span
>
</div>
<PipelineList :pipelines="pipelines" :repo="repo" />
<PipelineList :pipelines="pipelines" :repo="repo" />
</template>
<script lang="ts" setup>

View File

@ -1,23 +1,20 @@
<template>
<div class="space-y-4">
<template v-if="branches.length > 0">
<ListItem
<template v-if="branches.length > 0"
> <ListItem
v-for="branch in branchesWithDefaultBranchFirst"
:key="branch"
class="text-wp-text-100"
:to="{ name: 'repo-branch', params: { branch } }"
>
{{ branch }}
<Badge v-if="branch === repo?.default_branch" :label="$t('default')" class="ml-auto" />
</ListItem>
</template>
<div v-else-if="loading" class="flex justify-center text-wp-text-100">
<Icon name="spinner" />
</div>
<Panel v-else class="flex justify-center">
{{ $t('empty_list', { entity: $t('repo.branches') }) }}
</Panel>
> {{ branch }} <Badge v-if="branch === repo?.default_branch" :label="$t('default')" class="ml-auto" />
</ListItem
> </template
>
<div v-else-if="loading" class="flex justify-center text-wp-text-100"> <Icon name="spinner" /> </div>
<Panel v-else class="flex justify-center"> {{ $t('empty_list', { entity: $t('repo.branches') }) }} </Panel>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,7 @@
<template>
<div />
</template>
<script setup lang="ts">

View File

@ -1,27 +1,26 @@
<template>
<Panel v-if="!loading">
<Panel v-if="!loading"
>
<form @submit.prevent="triggerManualPipeline">
<span class="text-xl text-wp-text-100">{{ $t('repo.manual_pipeline.title') }}</span>
<InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.select_branch')">
<SelectField :id="id" v-model="payload.branch" :options="branches" required />
</InputField>
<InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.variables.title')">
<span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.manual_pipeline.variables.desc') }}</span>
<KeyValueEditor
<span class="text-xl text-wp-text-100">{{ $t('repo.manual_pipeline.title') }}</span
> <InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.select_branch')"
> <SelectField :id="id" v-model="payload.branch" :options="branches" required /> </InputField
> <InputField v-slot="{ id }" :label="$t('repo.manual_pipeline.variables.title')"
> <span class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.manual_pipeline.variables.desc') }}</span
> <KeyValueEditor
:id="id"
v-model="payload.variables"
:key-placeholder="$t('repo.manual_pipeline.variables.name')"
:value-placeholder="$t('repo.manual_pipeline.variables.value')"
:delete-title="$t('repo.manual_pipeline.variables.delete')"
@update:is-valid="isVariablesValid = $event"
/>
</InputField>
<Button type="submit" :text="$t('repo.manual_pipeline.trigger')" :disabled="!isFormValid" />
/> </InputField
> <Button type="submit" :text="$t('repo.manual_pipeline.trigger')" :disabled="!isFormValid" />
</form>
</Panel>
<div v-else class="flex justify-center text-wp-text-100">
<Icon name="spinner" />
</div>
</Panel
>
<div v-else class="flex justify-center text-wp-text-100"> <Icon name="spinner" /> </div>
</template>
<script lang="ts" setup>

View File

@ -1,6 +1,4 @@
<template>
<PipelineList :pipelines="pipelines" :repo="repo" />
</template>
<template> <PipelineList :pipelines="pipelines" :repo="repo" /> </template>
<script lang="ts" setup>
import { inject } from 'vue';

View File

@ -1,8 +1,10 @@
<template>
<div class="mb-4 flex w-full justify-center">
<span class="text-xl text-wp-text-100">{{ $t('repo.pipeline.pipelines_for_pr', { index: pullRequest }) }}</span>
<span class="text-xl text-wp-text-100">{{ $t('repo.pipeline.pipelines_for_pr', { index: pullRequest }) }}</span
>
</div>
<PipelineList :pipelines="pipelines" :repo="repo" />
<PipelineList :pipelines="pipelines" :repo="repo" />
</template>
<script lang="ts" setup>

View File

@ -1,28 +1,28 @@
<template>
<div class="space-y-4">
<template v-if="pullRequests.length > 0">
<ListItem
<template v-if="pullRequests.length > 0"
> <ListItem
v-for="pullRequest in pullRequests"
:key="pullRequest.index"
class="text-wp-text-100"
:to="{ name: 'repo-pull-request', params: { pullRequest: pullRequest.index } }"
>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span class="md:display-unset hidden text-wp-text-alt-100">#{{ pullRequest.index }}</span>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span class="md:display-unset mx-2 hidden text-wp-text-alt-100">-</span>
<span class="overflow-hidden overflow-ellipsis whitespace-nowrap text-wp-text-100 underline md:no-underline">{{
pullRequest.title
}}</span>
</ListItem>
</template>
<div v-else-if="loading" class="flex justify-center text-wp-text-100">
<Icon name="spinner" />
</div>
<Panel v-else class="flex justify-center">
{{ $t('empty_list', { entity: $t('repo.pull_requests') }) }}
</Panel>
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span
class="md:display-unset hidden text-wp-text-alt-100"
>#{{ pullRequest.index }}</span
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span
class="md:display-unset mx-2 hidden text-wp-text-alt-100"
>-</span
> <span
class="overflow-hidden overflow-ellipsis whitespace-nowrap text-wp-text-100 underline md:no-underline"
>{{ pullRequest.title }}</span
> </ListItem
> </template
>
<div v-else-if="loading" class="flex justify-center text-wp-text-100"> <Icon name="spinner" /> </div>
<Panel v-else class="flex justify-center"> {{ $t('empty_list', { entity: $t('repo.pull_requests') }) }} </Panel>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,54 +1,45 @@
<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
<template>
<Scaffold v-if="repo && repoPermissions && route.meta.repoHeader" enable-tabs>
<template #title>
<span class="flex">
<router-link
<Scaffold v-if="repo && repoPermissions && route.meta.repoHeader" enable-tabs
> <template #title
> <span class="flex"
> <router-link
:to="{ name: 'org', params: { orgId: repo.org_id } }"
class="hover:underline hover:decoration-wp-custom-highlight-100"
>{{ repo.owner }}</router-link
>
&nbsp;/
{{ repo.name }}
</span>
</template>
<template #headerActions>
<IconButton :href="repo.forge_url" :title="$t('repo.open_in_forge')" :icon="forgeIcon" class="forge h-8 w-8" />
> &nbsp;/ {{ repo.name }} </span
> </template
> <template #headerActions
> <IconButton :href="repo.forge_url" :title="$t('repo.open_in_forge')" :icon="forgeIcon" class="forge h-8 w-8" />
<IconButton
v-if="repoPermissions.admin"
:to="{ name: 'repo-settings' }"
:title="$t('settings')"
icon="settings"
/>
</template>
<template #tabActions>
<Button
/> </template
> <template #tabActions
> <Button
v-if="repoPermissions.push && route.name !== 'repo-manual'"
:text="$t('repo.manual_pipeline.trigger')"
start-icon="manual-pipeline"
:to="{ name: 'repo-manual' }"
/>
<Button
/> <Button
v-else-if="repoPermissions.push"
:text="$t('repo.manual_pipeline.show_pipelines')"
start-icon="back"
:to="{ name: 'repo' }"
/>
</template>
<Tab :to="{ name: 'repo' }" :title="$t('repo.activity')" />
<Tab :to="{ name: 'repo-branches' }" match-children :title="$t('repo.branches')" />
<Tab
/> </template
> <Tab :to="{ name: 'repo' }" :title="$t('repo.activity')" /> <Tab
:to="{ name: 'repo-branches' }"
match-children
:title="$t('repo.branches')"
/> <Tab
v-if="repo.pr_enabled && repo.allow_pr"
:to="{ name: 'repo-pull-requests' }"
match-children
:title="$t('repo.pull_requests')"
/>
<router-view />
</Scaffold>
<router-view v-else-if="repo && repoPermissions" />
/> <router-view /> </Scaffold
> <router-view v-else-if="repo && repoPermissions" />
</template>
<script lang="ts" setup>

View File

@ -1,64 +1,76 @@
<template>
<Container full-width class="md:min-h-xs flex flex-grow-0 flex-col md:flex-grow md:px-4">
<Container full-width class="md:min-h-xs flex flex-grow-0 flex-col md:flex-grow md:px-4"
>
<div class="flex min-h-0 w-full flex-grow flex-wrap-reverse md:flex-nowrap md:gap-4">
<PipelineStepList
<PipelineStepList
v-model:selected-step-id="selectedStepId"
:class="{ 'hidden md:flex': pipeline!.status === 'blocked' }"
:pipeline="pipeline!"
/>
<div class="relative flex flex-grow basis-full items-start justify-center md:basis-auto">
<div v-if="pipeline!.errors?.some((e) => !e.is_warning)" class="mb-4 w-full md:mb-auto">
<Panel>
<Panel
>
<div class="flex flex-col items-center gap-4 text-center">
<Icon name="status-error" class="h-16 w-16 text-wp-error-100" size="1.5rem" />
<span class="text-xl">{{ $t('repo.pipeline.we_got_some_errors') }}</span>
<Button color="red" :text="$t('repo.pipeline.show_errors')" :to="{ name: 'repo-pipeline-errors' }" />
<Icon name="status-error" class="h-16 w-16 text-wp-error-100" size="1.5rem" /> <span class="text-xl">{{
$t('repo.pipeline.we_got_some_errors')
}}</span
> <Button color="red" :text="$t('repo.pipeline.show_errors')" :to="{ name: 'repo-pipeline-errors' }" />
</div>
</Panel>
</Panel
>
</div>
<div v-else-if="pipeline!.status === 'blocked'" class="mb-4 w-full md:mb-auto">
<Panel>
<Panel
>
<div class="flex flex-col items-center gap-4">
<Icon name="status-blocked" size="1.5rem" class="h-16 w-16" />
<span class="text-xl">{{ $t('repo.pipeline.protected.awaits') }}</span>
<Icon name="status-blocked" size="1.5rem" class="h-16 w-16" /> <span class="text-xl">{{
$t('repo.pipeline.protected.awaits')
}}</span
>
<div v-if="repoPermissions!.push" class="flex flex-wrap items-center justify-center gap-2">
<Button
<Button
color="default"
:text="$t('repo.pipeline.protected.approve')"
:is-loading="isApprovingPipeline"
@click="approvePipeline"
/>
<Button
/> <Button
color="red"
:text="$t('repo.pipeline.protected.decline')"
:is-loading="isDecliningPipeline"
@click="declinePipeline"
/>
</div>
</div>
</Panel>
</Panel
>
</div>
<div v-else-if="pipeline!.status === 'declined'" class="mb-4 w-full md:mb-auto">
<Panel>
<Panel
>
<div class="flex flex-col items-center gap-4">
<Icon name="status-declined" size="1.5rem" class="h-16 w-16 text-wp-error-100" />
<Icon name="status-declined" size="1.5rem" class="h-16 w-16 text-wp-error-100" />
<p class="text-xl">{{ $t('repo.pipeline.protected.declined') }}</p>
</div>
</Panel>
</div>
<PipelineLog
</div>
</Panel
>
</div>
<PipelineLog
v-else-if="selectedStepId !== null"
v-model:step-id="selectedStepId"
:pipeline="pipeline!"
class="fixed left-0 top-0 h-full w-full md:absolute"
/>
</div>
</div>
</Container>
</Container
>
</template>
<script lang="ts" setup>

View File

@ -1,9 +1,13 @@
<template>
<Panel>
<Panel
>
<ul class="w-full list-inside list-disc">
<li v-for="file in pipeline!.changed_files" :key="file">{{ file }}</li>
</ul>
</Panel>
</Panel
>
</template>
<script lang="ts" setup>

View File

@ -1,15 +1,17 @@
<template>
<div class="flex flex-col gap-y-6">
<Panel
<Panel
v-for="pipelineConfig in pipelineConfigsDecoded"
:key="pipelineConfig.hash"
:collapsable="pipelineConfigsDecoded && pipelineConfigsDecoded.length > 1"
collapsed-by-default
:title="pipelineConfigsDecoded && pipelineConfigsDecoded.length > 1 ? pipelineConfig.name : ''"
> <SyntaxHighlight class="overflow-auto whitespace-pre font-mono" language="yaml" :code="pipelineConfig.data" />
</Panel
>
<SyntaxHighlight class="overflow-auto whitespace-pre font-mono" language="yaml" :code="pipelineConfig.data" />
</Panel>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,20 +1,33 @@
<template>
<template v-if="repoPermissions && repoPermissions.push">
<Panel>
<InputField :label="$t('repo.pipeline.debug.metadata_exec_title')">
<template v-if="repoPermissions && repoPermissions.push"
> <Panel
> <InputField :label="$t('repo.pipeline.debug.metadata_exec_title')"
>
<p class="mb-2 text-sm text-wp-text-alt-100">{{ $t('repo.pipeline.debug.metadata_exec_desc') }}</p>
<pre class="code-box">{{ cliExecWithMetadata }}</pre>
</InputField>
</InputField
>
<div class="flex items-center space-x-4">
<Button :is-loading="isLoading" :text="$t('repo.pipeline.debug.download_metadata')" @click="downloadMetadata" />
<Button
:is-loading="isLoading"
:text="$t('repo.pipeline.debug.download_metadata')"
@click="downloadMetadata"
/>
</div>
</Panel>
</template>
</Panel
> </template
>
<div v-else class="flex h-full items-center justify-center">
<div class="rounded-lg bg-wp-error-100 p-8 text-center shadow-lg dark:bg-wp-error-200">
<p class="text-2xl font-bold text-white">{{ $t('repo.pipeline.debug.no_permission') }}</p>
</div>
</div>
</template>
<script setup lang="ts">

View File

@ -1,50 +1,52 @@
<template>
<Panel>
<Panel
>
<div class="flex flex-col gap-y-4">
<template v-for="(error, _index) in pipeline!.errors" :key="_index">
<template v-for="(error, _index) in pipeline!.errors" :key="_index"
>
<div>
<div class="grid grid-cols-[minmax(10rem,auto),3fr]">
<span class="flex items-center gap-x-2">
<Icon
<span class="flex items-center gap-x-2"
> <Icon
name="alert"
class="my-1 flex-shrink-0"
:class="{
'text-wp-state-warn-100': error.is_warning,
'text-wp-error-100': !error.is_warning,
}"
/>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span>
<code>{{ error.type }}</code>
</span>
</span>
<span
/> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span
> <code>{{ error.type }}</code
> </span
> </span
> <span
v-if="isLinterError(error) || isDeprecationError(error) || isBadHabitError(error)"
class="flex items-center gap-x-2 whitespace-nowrap"
>
<span>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span v-if="error.data?.file" class="font-bold">{{ error.data?.file }}: </span>
<span>{{ error.data?.field }}</span>
</span>
<DocsLink
> <span
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span
v-if="error.data?.file"
class="font-bold"
>{{ error.data?.file }}: </span
> <span>{{ error.data?.field }}</span
> </span
> <DocsLink
v-if="isDeprecationError(error) || isBadHabitError(error)"
:topic="error.data?.field || ''"
:url="error.data?.docs || ''"
/>
</span>
<span v-else />
/> </span
> <span v-else />
</div>
<div class="col-start-2 grid grid-cols-[minmax(10rem,auto),4fr]">
<span />
<span>
<RenderMarkdown :content="error.message" />
</span>
<span /> <span> <RenderMarkdown :content="error.message" /> </span>
</div>
</div>
</template>
</template
>
</div>
</Panel>
</Panel
>
</template>
<script lang="ts" setup>

View File

@ -1,104 +1,97 @@
<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
<template>
<Scaffold
<Scaffold
v-if="pipeline && repo"
enable-tabs
:go-back="goBack"
:fluid-content="route.name === 'repo-pipeline'"
full-width-header
>
<template #title>
<span>
<router-link
> <template #title
> <span
> <router-link
:to="{ name: 'org', params: { orgId: repo.org_id } }"
class="hover:underline hover:decoration-wp-custom-highlight-100"
>{{ repo.owner }}</router-link
>
/
<router-link :to="{ name: 'repo' }" class="hover:underline">{{ repo.name }}</router-link>
</span>
</template>
<template #headerActions>
> / <router-link :to="{ name: 'repo' }" class="hover:underline">{{ repo.name }}</router-link
> </span
> </template
> <template #headerActions
>
<div class="flex w-full items-center justify-between gap-2">
<div class="flex min-w-0 content-start gap-2">
<PipelineStatusIcon :status="pipeline.status" class="flex flex-shrink-0" />
<span class="flex-shrink-0 text-center">{{ $t('repo.pipeline.pipeline', { pipelineId }) }}</span>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
<span class="hidden md:inline-block">-</span>
<span class="min-w-0 overflow-hidden overflow-ellipsis whitespace-nowrap" :title="message">{{
shortMessage
}}</span>
</div>
<template v-if="repoPermissions!.push && pipeline.status !== 'blocked'">
<div class="flex min-w-0 content-start gap-2">
<PipelineStatusIcon :status="pipeline.status" class="flex flex-shrink-0" /> <span
class="flex-shrink-0 text-center"
>{{ $t('repo.pipeline.pipeline', { pipelineId }) }}</span
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> <span class="hidden md:inline-block">-</span
> <span class="min-w-0 overflow-hidden overflow-ellipsis whitespace-nowrap" :title="message">{{
shortMessage
}}</span
>
</div>
<template v-if="repoPermissions!.push && pipeline.status !== 'blocked'"
>
<div class="flex content-start gap-x-2">
<Button
<Button
v-if="pipeline.status === 'pending' || pipeline.status === 'running'"
class="flex-shrink-0 !py-0"
:text="$t('repo.pipeline.actions.cancel')"
:is-loading="isCancelingPipeline"
@click="cancelPipeline"
/>
<Button
/> <Button
class="flex-shrink-0 !py-0"
:text="$t('repo.pipeline.actions.restart')"
:is-loading="isRestartingPipeline"
@click="restartPipeline"
/>
<Button
/> <Button
v-if="pipeline.status === 'success' && repo.allow_deploy"
class="flex-shrink-0 !py-0"
:text="$t('repo.pipeline.actions.deploy')"
@click="showDeployPipelinePopup = true"
/>
<DeployPipelinePopup
/> <DeployPipelinePopup
:pipeline-number="pipelineId"
:open="showDeployPipelinePopup"
@close="showDeployPipelinePopup = false"
/>
</div>
</template>
</template
>
</div>
</template>
<template #tabActions>
</template
> <template #tabActions
>
<div class="flex flex-wrap gap-4 md:flex-nowrap">
<div class="flex flex-shrink-0 items-center gap-2" :title="$t('repo.pipeline.created', { created })">
<Icon name="since" />
<span>{{ since }}</span>
</div>
<div class="flex flex-shrink-0 items-center gap-2" :title="$t('repo.pipeline.duration')">
<Icon name="duration" />
<span>{{ duration }}</span>
</div>
</div>
</template>
<Tab :to="{ name: 'repo-pipeline' }" :title="$t('repo.pipeline.tasks')" />
<Tab
<div class="flex flex-shrink-0 items-center gap-2" :title="$t('repo.pipeline.created', { created })">
<Icon name="since" /> <span>{{ since }}</span
>
</div>
<div class="flex flex-shrink-0 items-center gap-2" :title="$t('repo.pipeline.duration')">
<Icon name="duration" /> <span>{{ duration }}</span
>
</div>
</div>
</template
> <Tab :to="{ name: 'repo-pipeline' }" :title="$t('repo.pipeline.tasks')" /> <Tab
v-if="pipeline.errors && pipeline.errors.length > 0"
:to="{ name: 'repo-pipeline-errors' }"
icon="alert"
:title="pipeline.errors.some((e) => !e.is_warning) ? $t('repo.pipeline.errors') : $t('repo.pipeline.warnings')"
:count="pipeline.errors?.length"
:icon-class="pipeline.errors.some((e) => !e.is_warning) ? 'text-wp-error-100' : 'text-wp-state-warn-100'"
/>
<Tab :to="{ name: 'repo-pipeline-config' }" :title="$t('repo.pipeline.config')" />
<Tab
/> <Tab :to="{ name: 'repo-pipeline-config' }" :title="$t('repo.pipeline.config')" /> <Tab
v-if="pipeline.changed_files && pipeline.changed_files.length > 0"
:to="{ name: 'repo-pipeline-changed-files' }"
:title="$t('repo.pipeline.files')"
:count="pipeline.changed_files?.length"
/>
<Tab
/> <Tab
v-if="repoPermissions && repoPermissions.push"
:to="{ name: 'repo-pipeline-debug' }"
:title="$t('repo.pipeline.debug.title')"
/>
<router-view />
</Scaffold>
/> <router-view /> </Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,16 +1,15 @@
<template>
<Settings :title="$t('repo.settings.actions.actions')">
<Settings :title="$t('repo.settings.actions.actions')"
>
<div class="flex flex-wrap items-center">
<Button
<Button
class="my-1 mr-4"
color="default"
start-icon="heal"
:is-loading="isRepairingRepo"
:text="$t('repo.settings.actions.repair.repair')"
@click="repairRepo"
/>
<Button
/> <Button
v-if="isActive"
color="default"
class="my-1 mr-4"
@ -18,8 +17,7 @@
:is-loading="isDeactivatingRepo"
:text="$t('repo.settings.actions.disable.disable')"
@click="deactivateRepo"
/>
<Button
/> <Button
v-else
class="my-1 mr-4"
color="default"
@ -27,9 +25,7 @@
:is-loading="isActivatingRepo"
:text="$t('repo.settings.actions.enable.enable')"
@click="activateRepo"
/>
<Button
/> <Button
class="my-1 mr-4"
color="red"
start-icon="trash"
@ -38,7 +34,8 @@
@click="deleteRepo"
/>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,13 +1,9 @@
<template>
<Settings :title="$t('repo.settings.badge.badge')">
<template #titleActions>
<a v-if="badgeUrl" :href="badgeUrl" target="_blank">
<img :src="badgeUrl" />
</a>
</template>
<InputField v-slot="{ id }" :label="$t('repo.settings.badge.type')">
<SelectField
<Settings :title="$t('repo.settings.badge.badge')"
> <template #titleActions
> <a v-if="badgeUrl" :href="badgeUrl" target="_blank"> <img :src="badgeUrl" /> </a> </template
> <InputField v-slot="{ id }" :label="$t('repo.settings.badge.type')"
> <SelectField
:id="id"
v-model="badgeType"
:options="[
@ -25,18 +21,21 @@
},
]"
required
/>
</InputField>
<InputField v-slot="{ id }" :label="$t('repo.settings.badge.branch')">
<SelectField :id="id" v-model="branch" :options="branches" required />
</InputField>
/> </InputField
> <InputField v-slot="{ id }" :label="$t('repo.settings.badge.branch')"
> <SelectField :id="id" v-model="branch" :options="branches" required /> </InputField
>
<div v-if="badgeContent" class="flex flex-col space-y-4">
<div>
<pre class="code-box">{{ badgeContent }}</pre>
</div>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,107 +1,101 @@
<template>
<Settings
<Settings
:title="$t('repo.settings.crons.crons')"
:description="$t('repo.settings.crons.desc')"
docs-url="docs/usage/cron"
>
<template #headerActions>
<Button
> <template #headerActions
> <Button
v-if="selectedCron"
start-icon="back"
:text="$t('repo.settings.crons.show')"
@click="selectedCron = undefined"
/>
<Button v-else start-icon="plus" :text="$t('repo.settings.crons.add')" @click="selectedCron = {}" />
</template>
/> <Button v-else start-icon="plus" :text="$t('repo.settings.crons.add')" @click="selectedCron = {}" /> </template
>
<div v-if="!selectedCron" class="space-y-4 text-wp-text-100">
<ListItem
<ListItem
v-for="cron in crons"
:key="cron.id"
class="items-center !bg-wp-background-200 shadow-md dark:!bg-wp-background-100"
>
<span class="grid w-full grid-cols-3">
<span>{{ cron.name }}</span>
<span v-if="cron.next_exec && cron.next_exec > 0" class="md:display-unset col-span-2 hidden">
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
{{ $t('repo.settings.crons.next_exec') }}: {{ date.toLocaleString(new Date(cron.next_exec * 1000)) }}
</span>
<span v-else class="md:display-unset col-span-2 hidden">{{
> <span class="grid w-full grid-cols-3"
> <span>{{ cron.name }}</span
> <span v-if="cron.next_exec && cron.next_exec > 0" class="md:display-unset col-span-2 hidden"
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> {{ $t('repo.settings.crons.next_exec') }}:
{{ date.toLocaleString(new Date(cron.next_exec * 1000)) }} </span
> <span v-else class="md:display-unset col-span-2 hidden">{{
$t('repo.settings.crons.not_executed_yet')
}}</span>
</span>
<IconButton
}}</span
> </span
> <IconButton
icon="play-outline"
class="ml-auto h-8 w-8"
:title="$t('repo.settings.crons.run')"
@click="runCron(cron)"
/>
<IconButton icon="edit" class="h-8 w-8" :title="$t('repo.settings.crons.edit')" @click="selectedCron = cron" />
<IconButton
/> <IconButton
icon="edit"
class="h-8 w-8"
:title="$t('repo.settings.crons.edit')"
@click="selectedCron = cron"
/> <IconButton
icon="trash"
class="h-8 w-8 hover:text-wp-error-100"
:is-loading="isDeleting"
:title="$t('repo.settings.crons.delete')"
@click="deleteCron(cron)"
/>
</ListItem>
/> </ListItem
>
<div v-if="crons?.length === 0" class="ml-2">{{ $t('repo.settings.crons.none') }}</div>
</div>
<div v-else class="space-y-4">
<form @submit.prevent="createCron">
<InputField v-slot="{ id }" :label="$t('repo.settings.crons.name.name')">
<TextField
<InputField v-slot="{ id }" :label="$t('repo.settings.crons.name.name')"
> <TextField
:id="id"
v-model="selectedCron.name"
:placeholder="$t('repo.settings.crons.name.placeholder')"
required
/>
</InputField>
<InputField v-slot="{ id }" :label="$t('repo.settings.crons.branch.title')">
<TextField
/> </InputField
> <InputField v-slot="{ id }" :label="$t('repo.settings.crons.branch.title')"
> <TextField
:id="id"
v-model="selectedCron.branch"
:placeholder="$t('repo.settings.crons.branch.placeholder')"
/>
</InputField>
<InputField
/> </InputField
> <InputField
v-slot="{ id }"
:label="$t('repo.settings.crons.schedule.title')"
docs-url="https://pkg.go.dev/github.com/gdgvda/cron#hdr-CRON_Expression_Format"
>
<TextField
> <TextField
:id="id"
v-model="selectedCron.schedule"
:placeholder="$t('repo.settings.crons.schedule.placeholder')"
required
/>
</InputField>
/> </InputField
>
<div v-if="isEditingCron" class="mb-4 ml-auto">
<span v-if="selectedCron.next_exec && selectedCron.next_exec > 0" class="text-wp-text-100">
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
{{ $t('repo.settings.crons.next_exec') }}:
{{ date.toLocaleString(new Date(selectedCron.next_exec * 1000)) }}
</span>
<span v-else class="text-wp-text-100">{{ $t('repo.settings.crons.not_executed_yet') }}</span>
<span v-if="selectedCron.next_exec && selectedCron.next_exec > 0" class="text-wp-text-100"
> <!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text --> {{ $t('repo.settings.crons.next_exec') }}:
{{ date.toLocaleString(new Date(selectedCron.next_exec * 1000)) }} </span
> <span v-else class="text-wp-text-100">{{ $t('repo.settings.crons.not_executed_yet') }}</span
>
</div>
<div class="flex gap-2">
<Button type="button" color="gray" :text="$t('cancel')" @click="selectedCron = undefined" />
<Button
<Button type="button" color="gray" :text="$t('cancel')" @click="selectedCron = undefined" /> <Button
type="submit"
color="default"
:is-loading="isSaving"
:text="isEditingCron ? $t('repo.settings.crons.save') : $t('repo.settings.crons.add')"
/>
</div>
</form>
</div>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,64 +1,64 @@
<template>
<Settings :title="$t('repo.settings.general.project')">
<Settings :title="$t('repo.settings.general.project')"
>
<form v-if="repoSettings" class="flex flex-col" @submit.prevent="saveRepoSettings">
<InputField docs-url="usage/project-settings#general" :label="$t('repo.settings.general.general')">
<Checkbox
<InputField docs-url="usage/project-settings#general" :label="$t('repo.settings.general.general')"
> <Checkbox
v-model="repoSettings.allow_pr"
:label="$t('repo.settings.general.allow_pr.allow')"
:description="$t('repo.settings.general.allow_pr.desc')"
/>
<Checkbox
/> <Checkbox
v-model="repoSettings.allow_deploy"
:label="$t('repo.settings.general.allow_deploy.allow')"
:description="$t('repo.settings.general.allow_deploy.desc')"
/>
</InputField>
<InputField
/> </InputField
> <InputField
:label="$t('repo.settings.general.netrc_only_trusted.netrc_only_trusted')"
docs-url="usage/project-settings#custom-trusted-clone-plugins"
>
<template #default="{ id }">
> <template #default="{ id }"
>
<div class="flex flex-col gap-2">
<div v-for="image in repoSettings.netrc_trusted" :key="image" class="flex gap-2">
<TextField :id="id" :model-value="image" disabled />
<Button type="button" color="gray" start-icon="trash" @click="removeImage(image)" />
</div>
<div class="flex gap-2">
<TextField :id="id" v-model="newImage" @keydown.enter.prevent="addNewImage" />
<Button type="button" color="gray" start-icon="plus" @click="addNewImage" />
</div>
</div>
</template>
<template #description>
{{ $t('repo.settings.general.netrc_only_trusted.desc') }}
</template>
</InputField>
<InputField
<div v-for="image in repoSettings.netrc_trusted" :key="image" class="flex gap-2">
<TextField :id="id" :model-value="image" disabled /> <Button
type="button"
color="gray"
start-icon="trash"
@click="removeImage(image)"
/>
</div>
<div class="flex gap-2">
<TextField :id="id" v-model="newImage" @keydown.enter.prevent="addNewImage" /> <Button
type="button"
color="gray"
start-icon="plus"
@click="addNewImage"
/>
</div>
</div>
</template
> <template #description> {{ $t('repo.settings.general.netrc_only_trusted.desc') }} </template> </InputField
> <InputField
v-if="user?.admin"
docs-url="usage/repo-settings/#privileges-security"
:label="$t('repo.settings.general.trusted.trusted')"
>
<Checkbox
> <Checkbox
v-model="repoSettings.trusted.network"
:label="$t('repo.settings.general.trusted.network.network')"
:description="$t('repo.settings.general.trusted.network.desc')"
/>
<Checkbox
/> <Checkbox
v-model="repoSettings.trusted.volumes"
:label="$t('repo.settings.general.trusted.volumes.volumes')"
:description="$t('repo.settings.general.trusted.volumes.desc')"
/>
<Checkbox
/> <Checkbox
v-model="repoSettings.trusted.security"
:label="$t('repo.settings.general.trusted.security.security')"
:description="$t('repo.settings.general.trusted.security.desc')"
/>
</InputField>
<InputField :label="$t('require_approval.require_approval_for')">
<RadioField
/> </InputField
> <InputField :label="$t('require_approval.require_approval_for')"
> <RadioField
v-model="repoSettings.require_approval"
docs-url="usage/repo-settings/#approval-requirements"
:options="[
@ -80,63 +80,44 @@
text: $t('require_approval.all_events'),
},
]"
/>
<template #description>
{{ $t('require_approval.desc') }}
</template>
</InputField>
<InputField docs-url="usage/project-settings#project-visibility" :label="$t('repo.visibility.visibility')">
<RadioField v-model="repoSettings.visibility" :options="projectVisibilityOptions" />
</InputField>
<InputField
/> <template #description> {{ $t('require_approval.desc') }} </template> </InputField
> <InputField docs-url="usage/project-settings#project-visibility" :label="$t('repo.visibility.visibility')"
> <RadioField v-model="repoSettings.visibility" :options="projectVisibilityOptions" /> </InputField
> <InputField
v-slot="{ id }"
docs-url="usage/project-settings#timeout"
:label="$t('repo.settings.general.timeout.timeout')"
>
>
<div class="flex items-center">
<NumberField :id="id" v-model="repoSettings.timeout" class="w-24" />
<span class="ml-4 text-wp-text-alt-100">{{ $t('repo.settings.general.timeout.minutes') }}</span>
<NumberField :id="id" v-model="repoSettings.timeout" class="w-24" /> <span
class="ml-4 text-wp-text-alt-100"
>{{ $t('repo.settings.general.timeout.minutes') }}</span
>
</div>
</InputField>
<InputField
</InputField
> <InputField
docs-url="usage/project-settings#pipeline-path"
:label="$t('repo.settings.general.pipeline_path.path')"
>
<template #default="{ id }">
<TextField
> <template #default="{ id }"
> <TextField
:id="id"
v-model="repoSettings.config_file"
:placeholder="$t('repo.settings.general.pipeline_path.default')"
/>
</template>
<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
<template #description>
<i18n-t keypath="repo.settings.general.pipeline_path.desc">
<span class="code-box-inline">{{ $t('repo.settings.general.pipeline_path.desc_path_example') }}</span>
<span class="code-box-inline">/</span>
</i18n-t>
</template>
<!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
</InputField>
<InputField
/> </template
> <!-- eslint-disable @intlify/vue-i18n/no-raw-text --> <template #description
> <i18n-t keypath="repo.settings.general.pipeline_path.desc"
> <span class="code-box-inline">{{ $t('repo.settings.general.pipeline_path.desc_path_example') }}</span
> <span class="code-box-inline">/</span> </i18n-t
> </template
> <!-- eslint-enable @intlify/vue-i18n/no-raw-text --> </InputField
> <InputField
docs-url="usage/project-settings#cancel-previous-pipelines"
:label="$t('repo.settings.general.cancel_prev.cancel')"
>
<CheckboxesField
> <CheckboxesField
v-model="repoSettings.cancel_previous_pipeline_events"
:options="cancelPreviousPipelineEventsOptions"
/>
<template #description>
{{ $t('repo.settings.general.cancel_prev.desc') }}
</template>
</InputField>
<Button
/> <template #description> {{ $t('repo.settings.general.cancel_prev.desc') }} </template> </InputField
> <Button
type="submit"
class="mr-auto"
color="default"
@ -144,7 +125,8 @@
:text="$t('repo.settings.general.save')"
/>
</form>
</Settings>
</Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,31 +1,26 @@
<template>
<Settings :title="$t('registries.credentials')" :description="$t('registries.desc')" docs-url="docs/usage/registries">
<template #headerActions>
<Button
<Settings :title="$t('registries.credentials')" :description="$t('registries.desc')" docs-url="docs/usage/registries"
> <template #headerActions
> <Button
v-if="selectedRegistry"
:text="$t('registries.show')"
start-icon="back"
@click="selectedRegistry = undefined"
/>
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
</template>
<RegistryList
/> <Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" /> </template
> <RegistryList
v-if="!selectedRegistry"
v-model="registries"
:is-deleting="isDeleting"
@edit="editRegistry"
@delete="deleteRegistry"
/>
<RegistryEdit
/> <RegistryEdit
v-else
v-model="selectedRegistry"
:is-saving="isSaving"
@save="createRegistry"
@cancel="selectedRegistry = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,32 +1,29 @@
<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
<template>
<Scaffold enable-tabs :go-back="goBack">
<template #title>
<span>
<router-link
<Scaffold enable-tabs :go-back="goBack"
> <template #title
> <span
> <router-link
:to="{ name: 'org', params: { orgId: repo!.org_id } }"
class="hover:underline hover:decoration-wp-custom-highlight-100"
>{{ repo!.owner }}</router-link
>
/
<router-link :to="{ name: 'repo' }" class="hover:underline hover:decoration-wp-custom-highlight-100">{{
> / <router-link :to="{ name: 'repo' }" class="hover:underline hover:decoration-wp-custom-highlight-100">{{
repo!.name
/* eslint-disable-next-line @intlify/vue-i18n/no-raw-text */
}}</router-link>
/
{{ $t('settings') }}
</span>
</template>
<Tab :to="{ name: 'repo-settings' }" :title="$t('repo.settings.general.general')" />
<Tab :to="{ name: 'repo-settings-secrets' }" :title="$t('secrets.secrets')" />
<Tab :to="{ name: 'repo-settings-registries' }" :title="$t('registries.registries')" />
<Tab :to="{ name: 'repo-settings-crons' }" :title="$t('repo.settings.crons.crons')" />
<Tab :to="{ name: 'repo-settings-badge' }" :title="$t('repo.settings.badge.badge')" />
<Tab :to="{ name: 'repo-settings-actions' }" :title="$t('repo.settings.actions.actions')" />
<router-view />
</Scaffold>
}}</router-link
> / {{ $t('settings') }} </span
> </template
> <Tab :to="{ name: 'repo-settings' }" :title="$t('repo.settings.general.general')" /> <Tab
:to="{ name: 'repo-settings-secrets' }"
:title="$t('secrets.secrets')"
/> <Tab :to="{ name: 'repo-settings-registries' }" :title="$t('registries.registries')" /> <Tab
:to="{ name: 'repo-settings-crons' }"
:title="$t('repo.settings.crons.crons')"
/> <Tab :to="{ name: 'repo-settings-badge' }" :title="$t('repo.settings.badge.badge')" /> <Tab
:to="{ name: 'repo-settings-actions' }"
:title="$t('repo.settings.actions.actions')"
/> <router-view /> </Scaffold
>
</template>
<script lang="ts" setup>

View File

@ -1,26 +1,26 @@
<template>
<Settings :title="$t('secrets.secrets')" :description="$t('secrets.desc')" docs-url="docs/usage/secrets">
<template #headerActions>
<Button v-if="selectedSecret" :text="$t('secrets.show')" start-icon="back" @click="selectedSecret = undefined" />
<Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" />
</template>
<SecretList
<Settings :title="$t('secrets.secrets')" :description="$t('secrets.desc')" docs-url="docs/usage/secrets"
> <template #headerActions
> <Button
v-if="selectedSecret"
:text="$t('secrets.show')"
start-icon="back"
@click="selectedSecret = undefined"
/> <Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" /> </template
> <SecretList
v-if="!selectedSecret"
:model-value="secrets"
:is-deleting="isDeleting"
@edit="editSecret"
@delete="deleteSecret"
/>
<SecretEdit
/> <SecretEdit
v-else
v-model="selectedSecret"
:is-saving="isSaving"
@save="createSecret"
@cancel="selectedSecret = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<AgentManager
<AgentManager
:description="$t('user.settings.agents.desc')"
:load-agents="loadAgents"
:create-agent="createAgent"

View File

@ -1,35 +1,34 @@
<template>
<Settings :title="$t('user.settings.cli_and_api.cli_and_api')" :description="$t('user.settings.cli_and_api.desc')">
<InputField :label="$t('user.settings.cli_and_api.cli_usage')">
<template #headerActions>
<a :href="cliDownload" target="_blank" class="ml-4 text-wp-link-100 hover:text-wp-link-200">{{
<Settings :title="$t('user.settings.cli_and_api.cli_and_api')" :description="$t('user.settings.cli_and_api.desc')"
> <InputField :label="$t('user.settings.cli_and_api.cli_usage')"
> <template #headerActions
> <a :href="cliDownload" target="_blank" class="ml-4 text-wp-link-100 hover:text-wp-link-200">{{
$t('user.settings.cli_and_api.download_cli')
}}</a>
</template>
}}</a
> </template
>
<pre class="code-box">{{ usageWithCli }}</pre>
</InputField>
<InputField :label="$t('user.settings.cli_and_api.token')">
<template #titleActions>
<Button class="ml-auto" :text="$t('user.settings.cli_and_api.reset_token')" @click="resetToken" />
</template>
</InputField
> <InputField :label="$t('user.settings.cli_and_api.token')"
> <template #titleActions
> <Button class="ml-auto" :text="$t('user.settings.cli_and_api.reset_token')" @click="resetToken" /> </template
>
<pre class="code-box">{{ token }}</pre>
</InputField>
<InputField :label="$t('user.settings.cli_and_api.api_usage')">
<template #headerActions>
<a
</InputField
> <InputField :label="$t('user.settings.cli_and_api.api_usage')"
> <template #headerActions
> <a
v-if="enableSwagger"
:href="`${address}/swagger/index.html`"
target="_blank"
class="ml-4 text-wp-link-100 hover:text-wp-link-200"
>
{{ $t('user.settings.cli_and_api.swagger_ui') }}
</a>
</template>
> {{ $t('user.settings.cli_and_api.swagger_ui') }} </a
> </template
>
<pre class="code-box">{{ usageWithCurl }}</pre>
</InputField>
</Settings>
</InputField
> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,10 +1,9 @@
<template>
<Settings :title="$t('user.settings.general.general')">
<InputField v-slot="{ id }" :label="$t('user.settings.general.language')">
<SelectField :id="id" v-model="selectedLocale" :options="localeOptions" />
</InputField>
<InputField v-slot="{ id }" :label="$t('user.settings.general.theme.theme')">
<SelectField
<Settings :title="$t('user.settings.general.general')"
> <InputField v-slot="{ id }" :label="$t('user.settings.general.language')"
> <SelectField :id="id" v-model="selectedLocale" :options="localeOptions" /> </InputField
> <InputField v-slot="{ id }" :label="$t('user.settings.general.theme.theme')"
> <SelectField
:id="id"
v-model="storeTheme"
:options="[
@ -12,9 +11,9 @@
{ value: 'light', text: $t('user.settings.general.theme.light') },
{ value: 'dark', text: $t('user.settings.general.theme.dark') },
]"
/>
</InputField>
</Settings>
/> </InputField
> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,35 +1,29 @@
<template>
<Settings
<Settings
:title="$t('registries.registries')"
:description="$t('user.settings.registries.desc')"
docs-url="docs/usage/registries"
>
<template #headerActions>
<Button
> <template #headerActions
> <Button
v-if="selectedRegistry"
:text="$t('registries.show')"
start-icon="back"
@click="selectedRegistry = undefined"
/>
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
</template>
<RegistryList
/> <Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" /> </template
> <RegistryList
v-if="!selectedRegistry"
v-model="registries"
:is-deleting="isDeleting"
@edit="editRegistry"
@delete="deleteRegistry"
/>
<RegistryEdit
/> <RegistryEdit
v-else
v-model="selectedRegistry"
:is-saving="isSaving"
@save="createRegistry"
@cancel="selectedRegistry = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,30 +1,29 @@
<template>
<Settings
<Settings
:title="$t('secrets.secrets')"
:description="$t('user.settings.secrets.desc')"
docs-url="docs/usage/secrets"
>
<template #headerActions>
<Button v-if="selectedSecret" :text="$t('secrets.show')" start-icon="back" @click="selectedSecret = undefined" />
<Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" />
</template>
<SecretList
> <template #headerActions
> <Button
v-if="selectedSecret"
:text="$t('secrets.show')"
start-icon="back"
@click="selectedSecret = undefined"
/> <Button v-else :text="$t('secrets.add')" start-icon="plus" @click="showAddSecret" /> </template
> <SecretList
v-if="!selectedSecret"
v-model="secrets"
:is-deleting="isDeleting"
@edit="editSecret"
@delete="deleteSecret"
/>
<SecretEdit
/> <SecretEdit
v-else
v-model="selectedSecret"
:is-saving="isSaving"
@save="createSecret"
@cancel="selectedSecret = undefined"
/>
</Settings>
/> </Settings
>
</template>
<script lang="ts" setup>

View File

@ -1,20 +1,18 @@
<template>
<Scaffold enable-tabs>
<template #title>{{ $t('user.settings.settings') }}</template>
<template #headerActions><Button :text="$t('logout')" :to="`${address}/logout`" /></template>
<Tab :to="{ name: 'user' }" :title="$t('user.settings.general.general')" />
<Tab :to="{ name: 'user-secrets' }" :title="$t('secrets.secrets')" />
<Tab :to="{ name: 'user-registries' }" :title="$t('registries.registries')" />
<Tab :to="{ name: 'user-cli-and-api' }" :title="$t('user.settings.cli_and_api.cli_and_api')" />
<Tab
<Scaffold enable-tabs
> <template #title>{{ $t('user.settings.settings') }}</template
> <template #headerActions><Button :text="$t('logout')" :to="`${address}/logout`" /></template> <Tab
:to="{ name: 'user' }"
:title="$t('user.settings.general.general')"
/> <Tab :to="{ name: 'user-secrets' }" :title="$t('secrets.secrets')" /> <Tab
:to="{ name: 'user-registries' }"
:title="$t('registries.registries')"
/> <Tab :to="{ name: 'user-cli-and-api' }" :title="$t('user.settings.cli_and_api.cli_and_api')" /> <Tab
v-if="useConfig().userRegisteredAgents"
:to="{ name: 'user-agents' }"
:title="$t('admin.settings.agents.agents')"
/>
<router-view />
</Scaffold>
/> <router-view /> </Scaffold
>
</template>
<script lang="ts" setup>