<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useToast } from 'gnexus-ui-kit/vue'
import { api, type Task } from '../api'
import { formatMinutes, renderMarkdown, statusLabel } from '../taskui'
// Режим «3 варианта» (ТЗ 3.8): анти-прокрастинация — выбрать задачу под
// доступное время и делать её прямо сейчас.
const { t } = useI18n()
const toast = useToast()
const availableMinutes = ref(180)
const options = ref<Task[]>([])
const error = ref('')
const loading = ref(false)
const chosen = ref<Task | null>(null)
async function fetchOptions() {
loading.value = true
error.value = ''
options.value = []
try {
options.value = await api.suggestTasks(availableMinutes.value)
} catch (e) {
error.value = String(e)
} finally {
loading.value = false
}
}
onMounted(fetchOptions)
async function choose(task: Task) {
try {
chosen.value = await api.updateTask(task.id, { status: 'in_progress' })
toast.success({ title: t('common.taskTaken') })
options.value = []
} catch (e) {
toast.error({ title: t('common.error'), text: String(e) })
}
}
async function finishChosen() {
if (!chosen.value) return
try {
await api.updateTask(chosen.value.id, { status: 'done' })
toast.success({ title: t('common.taskFinished') })
chosen.value = null
await fetchOptions()
} catch (e) {
toast.error({ title: t('common.error'), text: String(e) })
}
}
async function dropChosen() {
if (!chosen.value) return
chosen.value = null
await fetchOptions()
}
</script>
<template>
<section>
<GnPageHeader kicker="gntodo" :title="t('options.title')" />
<!-- Выбранная задача: прямой переход к выполнению -->
<GnCard v-if="chosen" class="chosen-card">
<template #title>{{ chosen.title }}</template>
<div class="chosen-meta">
<GnBadge variant="accent">{{ statusLabel(chosen.status) }}</GnBadge>
<GnBadge v-if="chosen.estimated_minutes" variant="warning">
≈ {{ formatMinutes(chosen.estimated_minutes) }}
</GnBadge>
<GnBadge v-if="chosen.project" variant="neutral">{{ chosen.project.name }}</GnBadge>
</div>
<div v-if="chosen.description" class="md-preview" v-html="renderMarkdown(chosen.description)" />
<div class="form-actions">
<GnButton variant="accent" icon="ph-check-circle" @click="finishChosen">
{{ t('options.done') }}
</GnButton>
<GnButton variant="secondary" icon="ph-shuffle" @click="dropChosen">
{{ t('options.notNow') }}
</GnButton>
</div>
</GnCard>
<form v-else class="time-form" @submit.prevent="fetchOptions">
<GnInput
v-model.number="availableMinutes"
:label="t('options.available')"
type="number"
icon="ph-hourglass"
min="5"
max="1440"
/>
<GnButton type="submit" variant="primary" icon="ph-shuffle" :disabled="loading">
{{ t('options.suggest') }}
</GnButton>
</form>
<GnAlert v-if="error" variant="error">{{ error }}</GnAlert>
<template v-if="!chosen">
<GnSkeleton v-if="loading" type="block" stack :count="3" class="list-skeleton" />
<GnEmptyState
v-else-if="options.length === 0"
icon="ph-confetti"
:title="t('options.emptyTitle')"
:text="t('options.emptyText')"
/>
<div class="options">
<GnCard v-for="task in options" :key="task.id" class="option-card">
<template #title>{{ task.title }}</template>
<div class="option-meta">
<GnBadge v-if="task.estimated_minutes" variant="warning">
≈ {{ formatMinutes(task.estimated_minutes) }}
</GnBadge>
<GnBadge v-if="task.priority !== null" variant="info">P{{ task.priority }}</GnBadge>
<GnBadge v-if="task.project" variant="neutral">{{ task.project.name }}</GnBadge>
</div>
<GnButton variant="accent" icon="ph-play" @click="choose(task)">
{{ t('options.choose') }}
</GnButton>
</GnCard>
</div>
</template>
</section>
</template>
<style scoped>
.time-form {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: end;
margin-bottom: 1.5rem;
}
/* GnInput рендерит div.form-group > label > input — ограничиваем обёртку */
.time-form :deep(.form-group) {
width: 12rem;
margin-bottom: 0;
}
.options {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.list-skeleton {
margin-bottom: 1rem;
}
.option-meta,
.chosen-meta {
display: flex;
gap: 0.5rem;
margin-bottom: 0.75rem;
flex-wrap: wrap;
}
.chosen-card {
margin-bottom: 1.5rem;
}
.form-actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.md-preview {
border: 1px solid var(--border, #333);
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 0.75rem;
overflow-x: auto;
}
</style>