diff --git a/app/pages/admin/groups/[id].vue b/app/pages/admin/groups/[id].vue index 43a9cfe..eaa30cc 100644 --- a/app/pages/admin/groups/[id].vue +++ b/app/pages/admin/groups/[id].vue @@ -16,7 +16,7 @@ -
+

Участники ({{ members.length }})

@@ -29,13 +29,49 @@
{{ m.user.username }}
-
{{ m.role }}
+
+ + {{ m.role }} +
+ + +
+
+

Карты группы ({{ group.canvases?.length || 0 }})

+ Управлять в Проектах +
+
+
+
+
🗺️
+
+
{{ c.name }}
+
Обновлена: {{ new Date(c.updatedAt).toLocaleDateString() }}
+
+
+ +
+
+ В этой группе пока нет карт. +
+
+
@@ -139,6 +175,19 @@ const addMember = async (userId) => { } } +const updateMemberRole = async (userId, role) => { + try { + await $fetch(`/api/groups/${route.params.id}/members`, { + method: 'PATCH', + body: { userId, role }, + headers: { Authorization: `Bearer ${auth.token.value}` } + }) + await fetchGroup() + } catch (e) { + alert('Ошибка при смене роли') + } +} + const removeMember = async (userId) => { if (!confirm('Удалить участника из группы?')) return try { @@ -153,6 +202,20 @@ const removeMember = async (userId) => { } } +const unassignCanvas = async (canvasId) => { + if (!confirm('Отвязать карту от группы? Она станет личной картой владельца.')) return + try { + await $fetch(`/api/canvases/${canvasId}`, { + method: 'PATCH', + body: { groupId: null }, + headers: { Authorization: `Bearer ${auth.token.value}` } + }) + await fetchGroup() + } catch (e) { + alert('Ошибка при отвязке карты') + } +} + const fetchGroup = async () => { try { const data = await $fetch(`/api/groups/${route.params.id}`, { diff --git a/app/server/api/groups/[id]/index.get.ts b/app/server/api/groups/[id]/index.get.ts index 0380aa9..bedd040 100644 --- a/app/server/api/groups/[id]/index.get.ts +++ b/app/server/api/groups/[id]/index.get.ts @@ -11,13 +11,12 @@ export default defineEventHandler(async (event) => { members: { include: { user: { - select: { - id: true, - username: true, - email: true - } + select: { id: true, username: true, email: true } } } + }, + canvases: { + select: { id: true, name: true, updatedAt: true } } } }) diff --git a/app/server/api/groups/[id]/members.patch.ts b/app/server/api/groups/[id]/members.patch.ts new file mode 100644 index 0000000..5df1f54 --- /dev/null +++ b/app/server/api/groups/[id]/members.patch.ts @@ -0,0 +1,41 @@ +import { requireAuth } from '~/server/utils/auth' +import { prisma } from '~/server/utils/prisma' + +export default defineEventHandler(async (event) => { + const adminUser = requireAuth(event) + const groupId = getRouterParam(event, 'id') + const body = await readBody(event) + const { userId, role } = body + + if (!groupId || !userId || !role) { + throw createError({ statusCode: 400, message: 'Missing required fields' }) + } + + // 1. Проверяем права: только владелец группы или глобальный админ может менять роли + const group = await prisma.group.findUnique({ + where: { id: groupId } + }) + + if (!group || (group.ownerId !== adminUser.id && adminUser.role !== 'admin')) { + throw createError({ statusCode: 403, message: 'Permission denied' }) + } + + // 2. Нельзя менять роль самому владельцу (он всегда админ) + if (userId === group.ownerId) { + throw createError({ statusCode: 400, message: 'Cannot change owner role' }) + } + + // 3. Обновляем роль + try { + const updated = await prisma.groupMember.update({ + where: { + userId_groupId: { userId, groupId } + }, + data: { role } + }) + + return { success: true, updated } + } catch (e) { + throw createError({ statusCode: 500, message: 'Failed to update member role' }) + } +})