+
{{ t('stack.proposalPending') }}
diff --git a/frontend/src/views/TaskView.vue b/frontend/src/views/TaskView.vue
index 8d3748c..3e3fa83 100644
--- a/frontend/src/views/TaskView.vue
+++ b/frontend/src/views/TaskView.vue
@@ -398,28 +398,15 @@
return rows
})
-// Предложение ИИ — построчно с иконками (не сжатые бейджи)
+// Предложение ИИ — построчно с иконками (не сжатые бейджи). Заголовок и описание
+// модель не предлагает (0.93): контекста задачи у неё нет, сжатый текст теряет
+// детали. Теги дополняют назначенные, приоритет и оценка идут только в пустые поля.
const proposalRows = computed(() => {
const p = task.value?.ai_proposal
if (!p) return []
const rows: { icon: string; term: string; value: string }[] = []
- if (p.title)
- rows.push({ icon: 'ph-text-t', term: t('task.proposalNewTitle'), value: p.title })
- if (p.description)
- rows.push({
- icon: 'ph-list-numbers',
- term: t('task.proposalNewDescription'),
- // длинное описание сжимаем: полное попадёт в карточку «Описание» после утверждения
- value: p.description.length > 200 ? `${p.description.slice(0, 200)}…` : p.description,
- })
if (p.tags.length)
rows.push({ icon: 'ph-tag', term: t('stack.form.tags'), value: p.tags.join(', ') })
- if (p.project)
- rows.push({
- icon: p.new_project ? 'ph-folder-plus' : 'ph-folder',
- term: t('task.field.project'),
- value: p.new_project ? t('stack.proposalNewProject', { name: p.project }) : p.project,
- })
if (p.priority != null)
rows.push({
icon: 'ph-flag',
@@ -428,7 +415,7 @@
})
if (p.estimated_minutes !== null && p.estimated_minutes !== undefined)
rows.push({
- icon: 'ph-clock',
+ icon: 'ph-timer',
term: t('task.field.estimate'),
value: `≈ ${formatMinutes(p.estimated_minutes)}`,
})
@@ -444,16 +431,39 @@
}))
}
+// Карточка предложения: показываем, пока ИИ думает (ai_proposal === null), и когда
+// есть что предложить. Пустой ответ модели («добавить нечего») не рисуем вовсе —
+// пустая карточка только сбивает с толку; утвердить сырую задачу можно из меню шапки.
+const showProposal = computed(
+ () =>
+ task.value?.detail_state === 'raw' &&
+ (task.value.ai_proposal === null || proposalRows.value.length > 0),
+)
+
// Действия страницы — меню в шапке (статус вынесен в сайдбар)
function pageActions(): { label: string; icon: string; danger?: boolean; onSelect: () => void }[] {
const task0 = task.value
if (!task0) return []
- return [
+ const actions: { label: string; icon: string; danger?: boolean; onSelect: () => void }[] = [
{ label: t('common.edit'), icon: 'ph-pencil-simple', onSelect: () => (editing.value = true) },
{ label: t('task.ai.menu'), icon: 'ph-robot', onSelect: openAiPrompt },
{ label: t('stack.redetail'), icon: 'ph-arrows-counter-clockwise', onSelect: redetail },
- { label: t('common.delete'), icon: 'ph-trash', danger: true, onSelect: () => askDelete(task0) },
]
+ // ИИ предложить нечего (карточки нет) — сырую задачу всё равно надо чем-то
+ // утвердить: «Да, всё верно» из карточки в этом случае недоступно
+ if (task0.detail_state === 'raw')
+ actions.push({
+ label: t('stack.approve'),
+ icon: 'ph-check-circle',
+ onSelect: doItMyself,
+ })
+ actions.push({
+ label: t('common.delete'),
+ icon: 'ph-trash',
+ danger: true,
+ onSelect: () => askDelete(task0),
+ })
+ return actions
}
// «Промпт для ИИ-агента» (0.87): задача текстом — владелец вставляет его в чат со
@@ -828,14 +838,15 @@
-
-
+
+
{{ t('task.proposalTitle') }}
{{ t('task.proposalHint') }}
-