<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { api, type Task } from '../api'
import TaskCard from '../components/TaskCard.vue'
// «Выдача задачи» (ТЗ 3.8): под доступное время показываем те же карточки,
// что в списке (TaskCard). Взять в работу можно здесь через меню или на
// странице задачи — переход из выдачи несёт ?via=options, закрытие оттуда
// помечается «выбрал и сделал».
const { t } = useI18n()
// время в часах (можно 0.5); в API уходит в минутах
const availableHours = ref(3)
const options = ref<Task[]>([])
const error = ref('')
const loading = ref(false)
async function fetchOptions() {
loading.value = true
error.value = ''
try {
options.value = await api.suggestTasks(Math.round(availableHours.value * 60))
} catch (e) {
error.value = String(e)
} finally {
loading.value = false
}
}
onMounted(fetchOptions)
async function setStatus(task: Task, status: string) {
error.value = ''
try {
await api.updateTask(task.id, { status })
await fetchOptions()
} catch (e) {
error.value = String(e)
}
}
// Удаление — через подтверждение
const deleteTarget = ref<Task | null>(null)
const confirmOpen = ref(false)
function askDelete(task: Task) {
deleteTarget.value = task
confirmOpen.value = true
}
async function remove() {
const target = deleteTarget.value
if (!target) return
confirmOpen.value = false
error.value = ''
try {
await api.deleteTask(target.id)
await fetchOptions()
} catch (e) {
error.value = String(e)
}
}
</script>
<template>
<section>
<GnPageHeader kicker="GNexus Tasks" :title="t('options.title')" />
<!-- Доступное время в часах (0.5 = полчаса) + предложение, в одну строку -->
<form class="time-form" @submit.prevent="fetchOptions">
<GnInput
v-model.number="availableHours"
:label="t('options.availableHours')"
type="number"
step="0.5"
min="0.5"
max="24"
/>
<GnButton type="submit" variant="primary" icon="ph-shuffle" :disabled="loading">
{{ t('options.suggest') }}
</GnButton>
</form>
<GnAlert v-if="error" variant="error">{{ error }}</GnAlert>
<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')"
/>
<!-- Те же карточки, что в списке задач; открытие несёт ?via=options -->
<div class="task-list">
<TaskCard
v-for="task in options"
:key="task.id"
:task="task"
show-project
:link-query="{ via: 'options' }"
@status-change="setStatus"
@delete="askDelete"
/>
</div>
<GnConfirmDialog
v-model:open="confirmOpen"
:title="t('common.confirmTitle')"
:message="t('common.deleteConfirmTask', { title: deleteTarget?.title ?? '' })"
:confirm-text="t('common.delete')"
:cancel-text="t('common.cancel')"
confirm-variant="danger"
@confirm="remove"
/>
</section>
</template>
<style scoped>
.time-form {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
}
/* GnInput кита рендерит div.form-group > label > (текст + иконка + input).
Разворачиваем подпись и поле в один ряд: «Доступное время, часов ⏳ [3]»,
кнопка «Предложить» — следом, вся полоса одной строкой */
.time-form :deep(.form-group) {
flex: 0 0 auto;
width: max-content;
margin-bottom: 0;
}
.time-form :deep(.form-group .label) {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
white-space: nowrap;
}
.time-form :deep(input) {
width: 6rem;
}
.task-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.list-skeleton {
margin-bottom: 1rem;
}
</style>