feat: stabilize admin dashboard, refactor group management, implement PWA, and fix routing
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { requireAuth } from '~/server/utils/auth'
|
||||
import { prisma } from '~/server/utils/prisma'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = requireAuth(event)
|
||||
const body = await readBody(event)
|
||||
const { canvasId, title, priority, state } = body
|
||||
|
||||
if (!canvasId || !title) {
|
||||
throw createError({ statusCode: 400, message: 'Canvas ID and Title are required' })
|
||||
}
|
||||
|
||||
// 1. Получаем канвас
|
||||
const canvas = await prisma.canvas.findUnique({
|
||||
where: { id: canvasId }
|
||||
})
|
||||
|
||||
if (!canvas) {
|
||||
throw createError({ statusCode: 404, message: 'Canvas not found' })
|
||||
}
|
||||
|
||||
// 2. Проверяем права
|
||||
const isOwner = canvas.ownerId === user.id
|
||||
let isMember = false
|
||||
if (canvas.groupId) {
|
||||
const membership = await prisma.groupMember.findFirst({
|
||||
where: { userId: user.id, groupId: canvas.groupId }
|
||||
})
|
||||
isMember = !!membership
|
||||
}
|
||||
|
||||
if (!isOwner && !isMember && user.role !== 'admin') {
|
||||
throw createError({ statusCode: 403, message: 'No permission to add tasks to this canvas' })
|
||||
}
|
||||
|
||||
// 3. Создаем новый узел задачи
|
||||
const nodes = (canvas.nodes as any[]) || []
|
||||
|
||||
// Генерируем позицию (просто в центре или скраю)
|
||||
const lastNode = nodes[nodes.length - 1]
|
||||
const x = lastNode?.position?.x || 100
|
||||
const y = (lastNode?.position?.y || 100) + 100
|
||||
|
||||
const newTaskNode = {
|
||||
id: `task-${Date.now()}`,
|
||||
type: 'task',
|
||||
position: { x, y },
|
||||
data: {
|
||||
text: `# ${title}`,
|
||||
priority: priority || 'medium',
|
||||
state: state || 'todo',
|
||||
createdAt: new Date().toISOString()
|
||||
},
|
||||
// Дублируем поля для удобства фильтрации на бэкенде
|
||||
priority: priority || 'medium',
|
||||
state: state || 'todo',
|
||||
label: title
|
||||
}
|
||||
|
||||
nodes.push(newTaskNode)
|
||||
|
||||
// 4. Сохраняем в БД
|
||||
await prisma.canvas.update({
|
||||
where: { id: canvasId },
|
||||
data: { nodes }
|
||||
})
|
||||
|
||||
return { success: true, task: newTaskNode }
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import { requireAuth } from '~/server/utils/auth'
|
||||
import { prisma } from '~/server/utils/prisma'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = requireAuth(event)
|
||||
|
||||
// 1. Получаем группы пользователя
|
||||
const userGroups = await prisma.groupMember.findMany({
|
||||
where: { userId: user.id },
|
||||
select: { groupId: true }
|
||||
})
|
||||
const groupIds = userGroups.map(g => g.groupId)
|
||||
|
||||
// 2. Получаем все доступные канвасы
|
||||
const canvases = await prisma.canvas.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ ownerId: user.id },
|
||||
{ groupId: { in: groupIds } },
|
||||
{ isPublic: true }
|
||||
]
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
nodes: true
|
||||
}
|
||||
})
|
||||
|
||||
// 3. Собираем задачи из всех канвасов
|
||||
const allTasks: any[] = []
|
||||
|
||||
for (const canvas of canvases) {
|
||||
const nodes = (canvas.nodes as any[]) || []
|
||||
const taskNodes = nodes.filter(node => node.type === 'task')
|
||||
|
||||
taskNodes.forEach(task => {
|
||||
allTasks.push({
|
||||
...task,
|
||||
canvasId: canvas.id,
|
||||
canvasName: canvas.name
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Сортируем: сначала высокий приоритет, потом дата
|
||||
allTasks.sort((a, b) => {
|
||||
const pPriority = { high: 3, medium: 2, low: 1 }
|
||||
const prioA = pPriority[a.priority || a.data?.priority] || 0
|
||||
const prioB = pPriority[b.priority || b.data?.priority] || 0
|
||||
return prioB - prioA
|
||||
})
|
||||
|
||||
return { tasks: allTasks }
|
||||
})
|
||||
@@ -7,10 +7,10 @@ export default defineEventHandler(async (event) => {
|
||||
const { taskId, canvasId, newState } = body
|
||||
|
||||
if (!taskId || !canvasId || !newState) {
|
||||
throw createError({ statusCode: 400, message: 'taskId, canvasId and newState are required' })
|
||||
throw createError({ statusCode: 400, message: 'Missing parameters' })
|
||||
}
|
||||
|
||||
// 1. Найти холст
|
||||
// 1. Получаем канвас
|
||||
const canvas = await prisma.canvas.findUnique({
|
||||
where: { id: canvasId }
|
||||
})
|
||||
@@ -19,7 +19,21 @@ export default defineEventHandler(async (event) => {
|
||||
throw createError({ statusCode: 404, message: 'Canvas not found' })
|
||||
}
|
||||
|
||||
// 2. Найти и обновить узел в JSON
|
||||
// 2. Проверяем права на редактирование
|
||||
const isOwner = canvas.ownerId === user.id
|
||||
let isMember = false
|
||||
if (canvas.groupId) {
|
||||
const membership = await prisma.groupMember.findFirst({
|
||||
where: { userId: user.id, groupId: canvas.groupId }
|
||||
})
|
||||
isMember = !!membership
|
||||
}
|
||||
|
||||
if (!isOwner && !isMember && user.role !== 'admin') {
|
||||
throw createError({ statusCode: 403, message: 'No permission to update this task' })
|
||||
}
|
||||
|
||||
// 3. Обновляем узел внутри массива nodes
|
||||
const nodes = (canvas.nodes as any[]) || []
|
||||
const nodeIdx = nodes.findIndex(n => n.id === taskId)
|
||||
|
||||
@@ -29,18 +43,15 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
// Обновляем состояние
|
||||
nodes[nodeIdx].state = newState
|
||||
|
||||
// Также обновляем дату изменения, если нужно
|
||||
nodes[nodeIdx].updatedAt = new Date().toISOString()
|
||||
if (nodes[nodeIdx].data) {
|
||||
nodes[nodeIdx].data.state = newState
|
||||
}
|
||||
|
||||
// 3. Сохранить изменения в БД
|
||||
// 4. Сохраняем обратно в БД
|
||||
await prisma.canvas.update({
|
||||
where: { id: canvasId },
|
||||
data: { nodes }
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Task ${taskId} moved to ${newState}`
|
||||
}
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user