Compare commits

...

5 commits

Author SHA1 Message Date
Gros Frumos
a532f4299f kin: auto-commit after pipeline 2026-03-18 21:48:41 +02:00
Gros Frumos
2c08cc7ed5 Merge branch 'KIN-UI-022-frontend_dev' 2026-03-18 21:47:49 +02:00
Gros Frumos
229a3ee4ad kin: KIN-UI-022-frontend_dev 2026-03-18 21:47:49 +02:00
Gros Frumos
d374604741 Merge branch 'KIN-UI-024-frontend_dev' 2026-03-18 21:47:29 +02:00
Gros Frumos
aa6e4398bd kin: KIN-UI-024-frontend_dev 2026-03-18 21:47:29 +02:00
2 changed files with 17 additions and 10 deletions

View file

@ -320,15 +320,14 @@ describe('KIN-127: статус revising', () => {
expect(wrapper.find('a[href="/task/KIN-001"]').exists()).toBe(true) expect(wrapper.find('a[href="/task/KIN-001"]').exists()).toBe(true)
}) })
it('Badge для статуса revising присутствует в списке задач', async () => { it('Badge для статуса revising отображается с orange цветом', async () => {
const tasks = [makeTask('KIN-001', 'revising', null)] const tasks = [makeTask('KIN-001', 'revising', null)]
const wrapper = await mountTasks(tasks) const wrapper = await mountTasks(tasks)
// Badge с текстом revising должен присутствовать // Badge получает raw status string (decision #827: i18n на стороне вызывающего)
const text = wrapper.text() expect(wrapper.text()).toContain('revising')
expect(text).toContain('revising') // Badge с color="orange" применяет класс text-orange-400 (Badge.vue: colors.orange)
// Badge должен иметь orange цвет const orangeBadge = wrapper.find('.text-orange-400')
const orangeBadge = wrapper.find('.text-orange-400, .bg-orange-400, .bg-orange-500, .border-orange-400, .border-orange-500')
expect(orangeBadge.exists()).toBe(true) expect(orangeBadge.exists()).toBe(true)
}) })
}) })
@ -379,8 +378,11 @@ describe('KIN-127: защита от циклических ссылок', () =>
const wrapper = await mountTasks(tasks) const wrapper = await mountTasks(tasks)
// Рендер завершился без ошибок // Рендер завершился без ошибок
expect(wrapper.exists()).toBe(true) expect(wrapper.exists()).toBe(true)
// При циклической ссылке ни одна задача не является «корневой» с дочерними — // rootFilteredTasks пустой: оба KIN-001 и KIN-002 имеют parent_task_id
// toggle кнопок быть не должно (дефолтное состояние: дети не показаны) // указывающий на существующую задачу → оба отфильтрованы (decision #817)
const taskLinks = wrapper.findAll('a[href^="/task/"]')
expect(taskLinks.length).toBe(0)
// toggle кнопок нет — задачи не попали в список (decision #826: независимые visited Set)
const toggleBtns = wrapper.findAll('[data-testid="task-toggle-children"]') const toggleBtns = wrapper.findAll('[data-testid="task-toggle-children"]')
expect(toggleBtns.length).toBe(0) expect(toggleBtns.length).toBe(0)
}) })

View file

@ -769,6 +769,11 @@ function taskStatusColor(s: string) {
return m[s] || 'gray' return m[s] || 'gray'
} }
function taskStatusLabel(s: string) {
if (s === 'revising') return t('projectView.status_revising')
return s
}
function decTypeColor(t: string) { function decTypeColor(t: string) {
const m: Record<string, string> = { const m: Record<string, string> = {
decision: 'blue', gotcha: 'red', workaround: 'yellow', decision: 'blue', gotcha: 'red', workaround: 'yellow',
@ -1183,7 +1188,7 @@ async function addDecision() {
class="flex items-center justify-between px-3 py-2 border border-orange-800/60 bg-orange-950/20 rounded text-sm hover:border-orange-600 no-underline block transition-colors"> class="flex items-center justify-between px-3 py-2 border border-orange-800/60 bg-orange-950/20 rounded text-sm hover:border-orange-600 no-underline block transition-colors">
<div class="flex items-center gap-2 min-w-0"> <div class="flex items-center gap-2 min-w-0">
<span class="text-gray-500 shrink-0 w-24">{{ t.id }}</span> <span class="text-gray-500 shrink-0 w-24">{{ t.id }}</span>
<Badge :text="t.status" :color="taskStatusColor(t.status)" /> <Badge :text="taskStatusLabel(t.status)" :color="taskStatusColor(t.status)" />
<Badge v-if="t.category" :text="t.category" :color="CATEGORY_COLORS[t.category] || 'gray'" /> <Badge v-if="t.category" :text="t.category" :color="CATEGORY_COLORS[t.category] || 'gray'" />
<span class="text-orange-300 truncate">{{ t.title }}</span> <span class="text-orange-300 truncate">{{ t.title }}</span>
<span v-if="t.parent_task_id" class="text-[10px] text-gray-600 shrink-0">escalated from {{ t.parent_task_id }}</span> <span v-if="t.parent_task_id" class="text-[10px] text-gray-600 shrink-0">escalated from {{ t.parent_task_id }}</span>
@ -1211,7 +1216,7 @@ async function addDecision() {
</button> </button>
<span v-else class="w-4 shrink-0"></span> <span v-else class="w-4 shrink-0"></span>
<span class="text-gray-500 shrink-0 w-24">{{ t.id }}</span> <span class="text-gray-500 shrink-0 w-24">{{ t.id }}</span>
<Badge :text="t.status" :color="taskStatusColor(t.status)" /> <Badge :text="taskStatusLabel(t.status)" :color="taskStatusColor(t.status)" />
<Badge v-if="t.category" :text="t.category" :color="CATEGORY_COLORS[t.category] || 'gray'" /> <Badge v-if="t.category" :text="t.category" :color="CATEGORY_COLORS[t.category] || 'gray'" />
<span class="text-gray-300 truncate">{{ t.title }}</span> <span class="text-gray-300 truncate">{{ t.title }}</span>
<span v-if="t.execution_mode === 'auto_complete'" <span v-if="t.execution_mode === 'auto_complete'"