feat: stabilize admin dashboard, refactor group management, implement PWA, and fix routing
This commit is contained in:
+94
-1
@@ -5,6 +5,56 @@
|
||||
<h1 class="text-3xl font-bold text-white flex items-center gap-3">
|
||||
<span class="text-blue-500">📋</span> Мои Задачи
|
||||
</h1>
|
||||
<div class="flex items-center gap-3">
|
||||
<button @click="showCreateModal = true" class="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-medium shadow-lg shadow-blue-900/20 transition flex items-center gap-2">
|
||||
<span>➕</span> Новая задача
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Task Modal -->
|
||||
<div v-if="showCreateModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4" @click.self="showCreateModal = false">
|
||||
<div class="bg-slate-800 border border-slate-700 rounded-2xl p-6 w-full max-w-md shadow-2xl">
|
||||
<h2 class="text-xl font-bold text-white mb-6">Создать новую задачу</h2>
|
||||
<form @submit.prevent="createTask" class="space-y-4">
|
||||
<div>
|
||||
<label class="text-xs text-slate-500 uppercase font-bold">Название задачи</label>
|
||||
<input v-model="newTask.title" required class="w-full mt-1 bg-slate-900 border border-slate-700 rounded-lg px-4 py-2.5 text-slate-200 outline-none focus:border-blue-500" placeholder="Что нужно сделать?" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="text-xs text-slate-500 uppercase font-bold">Проект (Канвас)</label>
|
||||
<select v-model="newTask.canvasId" required class="w-full mt-1 bg-slate-900 border border-slate-700 rounded-lg px-4 py-2.5 text-slate-200 outline-none focus:border-blue-500">
|
||||
<option value="" disabled>Выберите проект...</option>
|
||||
<option v-for="c in availableCanvases" :key="c.id" :value="c.id">{{ c.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-xs text-slate-500 uppercase font-bold">Приоритет</label>
|
||||
<select v-model="newTask.priority" class="w-full mt-1 bg-slate-900 border border-slate-700 rounded-lg px-4 py-2.5 text-slate-200 outline-none focus:border-blue-500">
|
||||
<option value="low">Низкий</option>
|
||||
<option value="medium">Средний</option>
|
||||
<option value="high">Высокий</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-slate-500 uppercase font-bold">Статус</label>
|
||||
<select v-model="newTask.state" class="w-full mt-1 bg-slate-900 border border-slate-700 rounded-lg px-4 py-2.5 text-slate-200 outline-none focus:border-blue-500">
|
||||
<option v-for="col in columns" :key="col.id" :value="col.id">{{ col.title }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="submit" :disabled="creating" class="flex-1 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold transition disabled:opacity-50">
|
||||
{{ creating ? 'Создание...' : 'Создать задачу' }}
|
||||
</button>
|
||||
<button type="button" @click="showCreateModal = false" class="px-6 py-3 bg-slate-700 hover:bg-slate-600 text-white rounded-xl font-bold transition">Отмена</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="flex items-center justify-center h-64">
|
||||
@@ -92,6 +142,16 @@ const columnsData = reactive({
|
||||
completed: []
|
||||
})
|
||||
|
||||
const showCreateModal = ref(false)
|
||||
const creating = ref(false)
|
||||
const availableCanvases = ref([])
|
||||
const newTask = reactive({
|
||||
title: '',
|
||||
canvasId: '',
|
||||
priority: 'medium',
|
||||
state: 'todo'
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{ id: 'todo', title: 'К выполнению', icon: '📋', color: 'text-slate-400' },
|
||||
{ id: 'in_progress', title: 'В работе', icon: '⏳', color: 'text-blue-400' },
|
||||
@@ -125,6 +185,36 @@ const fetchTasks = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchCanvases = async () => {
|
||||
try {
|
||||
const res = await $fetch('/api/canvas')
|
||||
availableCanvases.value = res.canvases || []
|
||||
if (availableCanvases.value.length > 0) {
|
||||
newTask.canvasId = availableCanvases.value[0].id
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to load canvases', e)
|
||||
}
|
||||
}
|
||||
|
||||
const createTask = async () => {
|
||||
creating.value = true
|
||||
try {
|
||||
await $fetch('/api/tasks/create', {
|
||||
method: 'POST',
|
||||
body: newTask,
|
||||
headers: { Authorization: `Bearer ${auth.token.value}` }
|
||||
})
|
||||
showCreateModal.value = false
|
||||
newTask.title = ''
|
||||
await fetchTasks() // Refresh list
|
||||
} catch (e) {
|
||||
alert('Ошибка при создании задачи: ' + (e.data?.message || e.message))
|
||||
} finally {
|
||||
creating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onTaskMove = async (evt, newStatus) => {
|
||||
if (evt.added) {
|
||||
const task = evt.added.element
|
||||
@@ -197,7 +287,10 @@ onMounted(async () => {
|
||||
if (!auth.user.value) {
|
||||
await auth.init()
|
||||
}
|
||||
await fetchTasks()
|
||||
await Promise.all([
|
||||
fetchTasks(),
|
||||
fetchCanvases()
|
||||
])
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user