feat: stabilize admin dashboard, refactor group management, implement PWA, and fix routing

This commit is contained in:
2026-05-03 19:51:18 +05:00
parent 622ca04170
commit 0598e07823
36 changed files with 1387 additions and 467 deletions
+94 -1
View File
@@ -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>