kin: auto-commit after pipeline

This commit is contained in:
Gros Frumos 2026-03-17 17:53:14 +02:00
parent 4144c521be
commit 3c902eaeab
6 changed files with 1354 additions and 7 deletions

View file

@ -1348,6 +1348,79 @@ async function addDecision() {
</div>
</div>
<!-- Links Tab -->
<div v-if="activeTab === 'links'">
<div class="flex items-center justify-between mb-3">
<span class="text-xs text-gray-500">Связи между проектами</span>
<button @click="showAddLink = true"
class="px-3 py-1 text-xs bg-gray-800 text-gray-300 border border-gray-700 rounded hover:bg-gray-700">
+ Add Link
</button>
</div>
<p v-if="linksLoading" class="text-gray-500 text-sm">Загрузка...</p>
<p v-else-if="linksError" class="text-red-400 text-sm">{{ linksError }}</p>
<div v-else-if="links.length === 0" class="text-gray-600 text-sm">Нет связей. Добавьте зависимости между проектами.</div>
<div v-else class="space-y-2">
<div v-for="link in links" :key="link.id"
class="px-4 py-3 border border-gray-800 rounded hover:border-gray-700 flex items-center justify-between gap-3">
<div class="flex items-center gap-2 text-sm flex-wrap">
<span class="text-gray-400 font-mono text-xs">{{ link.from_project }}</span>
<span class="text-gray-600">-></span>
<span class="text-gray-400 font-mono text-xs">{{ link.to_project }}</span>
<span class="px-1.5 py-0.5 text-[10px] bg-indigo-900/30 text-indigo-400 border border-indigo-800 rounded">{{ link.link_type }}</span>
<span v-if="link.description" class="text-gray-500 text-xs">{{ link.description }}</span>
</div>
<button @click="deleteLink(link.id)"
class="px-2 py-0.5 text-xs bg-gray-800 text-red-500 border border-gray-700 rounded hover:bg-red-950/30 hover:border-red-800 shrink-0">
x
</button>
</div>
</div>
<!-- Add Link Modal -->
<Modal v-if="showAddLink" title="Add Link" @close="showAddLink = false; linkForm = { to_project: '', link_type: 'depends_on', description: '' }; linkFormError = ''">
<form @submit.prevent="addLink" class="space-y-3">
<div>
<label class="block text-xs text-gray-500 mb-1">From (current project)</label>
<input :value="props.id" disabled
class="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-500 font-mono" />
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">To project</label>
<select v-model="linkForm.to_project" required
class="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-300">
<option value=""> выберите проект </option>
<option v-for="p in allProjects.filter(p => p.id !== props.id)" :key="p.id" :value="p.id">{{ p.id }} {{ p.name }}</option>
</select>
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">Link type</label>
<select v-model="linkForm.link_type"
class="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-300">
<option value="depends_on">depends_on</option>
<option value="triggers">triggers</option>
<option value="related_to">related_to</option>
</select>
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">Description (optional)</label>
<input v-model="linkForm.description" placeholder="e.g. API used by frontend"
class="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-200 placeholder-gray-600" />
</div>
<p v-if="linkFormError" class="text-red-400 text-xs">{{ linkFormError }}</p>
<div class="flex gap-2 justify-end">
<button type="button" @click="showAddLink = false; linkFormError = ''"
class="px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200">Отмена</button>
<button type="submit" :disabled="linkSaving"
class="px-4 py-1.5 text-sm bg-blue-900/50 text-blue-400 border border-blue-800 rounded hover:bg-blue-900 disabled:opacity-50">
{{ linkSaving ? 'Saving...' : 'Add Link' }}
</button>
</div>
</form>
</Modal>
</div>
<!-- Add Task Modal -->
<Modal v-if="showAddTask" title="Add Task" @close="closeAddTaskModal">
<form @submit.prevent="addTask" class="space-y-3">

View file

@ -581,16 +581,55 @@ async function saveEdit() {
<!-- Deploy result inline block -->
<div v-if="deployResult" class="mx-0 mt-2 p-3 rounded border text-xs font-mono"
:class="deployResult.success ? 'border-teal-800 bg-teal-950/30 text-teal-300' : 'border-red-800 bg-red-950/30 text-red-300'">
:class="deployResult.overall_success !== false && deployResult.success ? 'border-teal-800 bg-teal-950/30 text-teal-300' : 'border-red-800 bg-red-950/30 text-red-300'">
<div class="flex items-center gap-2 mb-1">
<span :class="deployResult.success ? 'text-teal-400' : 'text-red-400'" class="font-semibold">
{{ deployResult.success ? 'Deploy succeeded' : 'Deploy failed' }}
<span :class="deployResult.overall_success !== false && deployResult.success ? 'text-teal-400' : 'text-red-400'" class="font-semibold">
{{ deployResult.overall_success !== false && deployResult.success ? 'Deploy succeeded' : 'Deploy failed' }}
</span>
<span class="text-gray-500">exit {{ deployResult.exit_code }} · {{ deployResult.duration_seconds }}s</span>
<button @click.stop="deployResult = null" class="ml-auto text-gray-600 hover:text-gray-400 bg-transparent border-none cursor-pointer text-xs"></button>
<span class="text-gray-500">{{ deployResult.duration_seconds }}s</span>
<button @click.stop="deployResult = null" class="ml-auto text-gray-600 hover:text-gray-400 bg-transparent border-none cursor-pointer text-xs">x</button>
</div>
<!-- Structured steps -->
<div v-if="deployResult.results?.length" class="space-y-1 mt-1">
<details v-for="step in deployResult.results" :key="step.step" class="border border-gray-700 rounded">
<summary class="flex items-center gap-2 px-2 py-1 cursor-pointer list-none">
<span :class="step.exit_code === 0 ? 'text-teal-400' : 'text-red-400'" class="font-semibold text-[10px]">{{ step.exit_code === 0 ? 'ok' : 'fail' }}</span>
<span class="text-gray-300 text-[11px]">{{ step.step }}</span>
<span class="text-gray-600 text-[10px] ml-auto">exit {{ step.exit_code }}</span>
</summary>
<div class="px-2 pb-2">
<pre v-if="step.stdout" class="whitespace-pre-wrap text-gray-300 max-h-32 overflow-y-auto text-[10px]">{{ step.stdout }}</pre>
<pre v-if="step.stderr" class="whitespace-pre-wrap text-red-400/80 max-h-32 overflow-y-auto text-[10px] mt-1">{{ step.stderr }}</pre>
</div>
</details>
</div>
<!-- Legacy output -->
<template v-else>
<pre v-if="deployResult.stdout" class="whitespace-pre-wrap text-gray-300 max-h-40 overflow-y-auto">{{ deployResult.stdout }}</pre>
<pre v-if="deployResult.stderr" class="whitespace-pre-wrap text-red-400/80 max-h-40 overflow-y-auto mt-1">{{ deployResult.stderr }}</pre>
</template>
<!-- Dependents -->
<div v-if="deployResult.dependents_deployed?.length" class="mt-2 border-t border-gray-700 pt-2">
<p class="text-xs text-gray-400 font-semibold mb-1">Зависимые проекты:</p>
<details v-for="dep in deployResult.dependents_deployed" :key="dep.project_id" class="border border-gray-700 rounded mb-1">
<summary class="flex items-center gap-2 px-2 py-1 cursor-pointer list-none">
<span :class="dep.success ? 'text-teal-400' : 'text-red-400'" class="font-semibold text-[10px]">{{ dep.success ? 'ok' : 'fail' }}</span>
<span class="text-gray-300 text-[11px]">{{ dep.project_name }}</span>
</summary>
<div class="px-2 pb-2 space-y-1">
<details v-for="step in dep.results" :key="step.step" class="border border-gray-800 rounded">
<summary class="flex items-center gap-2 px-2 py-0.5 cursor-pointer list-none">
<span :class="step.exit_code === 0 ? 'text-teal-400' : 'text-red-400'" class="text-[10px]">{{ step.exit_code === 0 ? 'ok' : 'fail' }}</span>
<span class="text-gray-400 text-[10px]">{{ step.step }}</span>
</summary>
<div class="px-2 pb-1">
<pre v-if="step.stdout" class="whitespace-pre-wrap text-gray-300 max-h-24 overflow-y-auto text-[10px]">{{ step.stdout }}</pre>
<pre v-if="step.stderr" class="whitespace-pre-wrap text-red-400/80 max-h-24 overflow-y-auto text-[10px]">{{ step.stderr }}</pre>
</div>
</details>
</div>
</details>
</div>
<pre v-if="deployResult.stdout" class="whitespace-pre-wrap text-gray-300 max-h-40 overflow-y-auto">{{ deployResult.stdout }}</pre>
<pre v-if="deployResult.stderr" class="whitespace-pre-wrap text-red-400/80 max-h-40 overflow-y-auto mt-1">{{ deployResult.stderr }}</pre>
</div>
<!-- Approve Modal -->