feat: добавил кабинеты преподавателя и администратора
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 38s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 18s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 6s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 8s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 38s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 18s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 6s
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { usersApi } from '@/api'
|
||||
import type { UserDto } from '@/api/types'
|
||||
import GlassCard from '@/components/ui/GlassCard.vue'
|
||||
import DataTable from '@/components/ui/DataTable.vue'
|
||||
import EmptyState from '@/components/ui/EmptyState.vue'
|
||||
|
||||
const search = ref('')
|
||||
const roleFilter = ref('Все роли')
|
||||
const instituteFilter = ref('Все институты')
|
||||
const users = ref<UserDto[]>([])
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Имя' },
|
||||
@@ -17,11 +23,51 @@ const columns = [
|
||||
{ key: 'actions', label: 'Действия', align: 'right' },
|
||||
]
|
||||
|
||||
const rows = [
|
||||
{ id: '1', name: 'Алексей Морозов', email: 'a.morozov@sfedu.ru', role: 'Студент', institute: 'ИКТИБ', activity: 'Высокая', created: '12.03.2024' },
|
||||
{ id: '2', name: 'Елена Смирнова', email: 'e.smirnova@sfedu.ru', role: 'Преподаватель', institute: 'АГиС', activity: 'Средняя', created: '05.02.2023' },
|
||||
{ id: '3', name: 'Виктор Алексеев', email: 'admin@sfedu.ru', role: 'Администратор', institute: 'ЮФУ', activity: 'Высокая', created: '01.09.2022' },
|
||||
]
|
||||
const roleLabels = { Student: 'Студент', Teacher: 'Преподаватель', Admin: 'Администратор' } as const
|
||||
const roleApi = { Студент: 'Student', Преподаватель: 'Teacher', Администратор: 'Admin' } as const
|
||||
|
||||
const rows = computed(() =>
|
||||
users.value.map(user => ({
|
||||
id: user.id,
|
||||
name: user.displayName || user.email,
|
||||
email: user.email,
|
||||
role: roleLabels[user.role],
|
||||
apiRole: user.role,
|
||||
institute: 'ЮФУ',
|
||||
activity: user.isActive ? 'Активен' : 'Заблокирован',
|
||||
isActive: user.isActive,
|
||||
created: new Date(user.createdAt).toLocaleDateString('ru-RU'),
|
||||
})),
|
||||
)
|
||||
|
||||
async function fetchUsers() {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
users.value = await usersApi.list({
|
||||
Search: search.value || undefined,
|
||||
Role: roleFilter.value === 'Все роли' ? undefined : roleApi[roleFilter.value as keyof typeof roleApi],
|
||||
PageSize: 100,
|
||||
})
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Не удалось загрузить пользователей.'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleActive(row: Record<string, any>) {
|
||||
await usersApi.setActive(row.id, !row.isActive)
|
||||
await fetchUsers()
|
||||
}
|
||||
|
||||
async function promoteRole(row: Record<string, any>) {
|
||||
const next = row.apiRole === 'Student' ? 'Teacher' : row.apiRole === 'Teacher' ? 'Admin' : 'Student'
|
||||
await usersApi.setRole(row.id, next)
|
||||
await fetchUsers()
|
||||
}
|
||||
|
||||
onMounted(fetchUsers)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,7 +80,7 @@ const rows = [
|
||||
<GlassCard>
|
||||
<div class="filters">
|
||||
<input v-model="search" class="glass-input" placeholder="Поиск по имени или email" />
|
||||
<select v-model="roleFilter" class="glass-input">
|
||||
<select v-model="roleFilter" class="glass-input" @change="fetchUsers">
|
||||
<option>Все роли</option>
|
||||
<option>Студент</option>
|
||||
<option>Преподаватель</option>
|
||||
@@ -49,18 +95,19 @@ const rows = [
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<EmptyState v-if="error" title="Не удалось загрузить пользователей" :subtitle="error" />
|
||||
<EmptyState v-else-if="!rows.length && !loading" title="Пользователей не найдено" subtitle="Попробуйте изменить фильтры." />
|
||||
<DataTable :columns="columns" :rows="rows">
|
||||
<template #role="{ value }">
|
||||
<span :class="value === 'Студент' ? 'badge badge-green' : value === 'Преподаватель' ? 'badge badge-blue' : 'badge badge-purple'">{{ value }}</span>
|
||||
</template>
|
||||
<template #activity="{ value }">
|
||||
<span class="badge" :class="value === 'Высокая' ? 'badge-green' : 'badge-orange'">{{ value }}</span>
|
||||
<span class="badge" :class="value === 'Активен' ? 'badge-green' : 'badge-orange'">{{ value }}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<template #actions="{ row }">
|
||||
<div class="actions">
|
||||
<button class="btn-ghost">Назначить роль</button>
|
||||
<button class="btn-ghost">Заблокировать</button>
|
||||
<button class="btn-ghost">Профиль</button>
|
||||
<button class="btn-ghost" @click="promoteRole(row)">Назначить роль</button>
|
||||
<button class="btn-ghost" @click="toggleActive(row)">{{ row.isActive ? 'Заблокировать' : 'Активировать' }}</button>
|
||||
</div>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
Reference in New Issue
Block a user