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

This commit is contained in:
2026-05-11 01:58:09 +03:00
parent 779b6aba77
commit 610c15c9fd
11 changed files with 399 additions and 90 deletions
@@ -1,12 +1,28 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useLecturesStore } from '@/stores/lectures'
import { useAuthStore } from '@/stores/auth'
import GlassCard from '@/components/ui/GlassCard.vue'
import StatsWidget from '@/components/ui/StatsWidget.vue'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import EmptyState from '@/components/ui/EmptyState.vue'
const lecturesStore = useLecturesStore()
const upcoming = computed(() => lecturesStore.all.slice(0, 3))
const auth = useAuthStore()
const router = useRouter()
const teacherLectures = computed(() => {
const owned = lecturesStore.all.filter(l => auth.user && l.teacher.includes(auth.user.name))
return owned.length ? owned : lecturesStore.all
})
const upcoming = computed(() => teacherLectures.value.filter(l => l.status !== 'completed').slice(0, 3))
const enrolledTotal = computed(() => teacherLectures.value.reduce((sum, l) => sum + (l.totalSeats - l.freeSeats), 0))
const visibility = computed(() => (teacherLectures.value.length ? Math.min(100, Math.round(enrolledTotal.value * 4)) : 0))
onMounted(() => {
if (!lecturesStore.all.length) void lecturesStore.fetchLectures()
})
</script>
<template>
@@ -14,39 +30,39 @@ const upcoming = computed(() => lecturesStore.all.slice(0, 3))
<div class="header">
<h1 class="page-title">Дашборд преподавателя</h1>
<div class="actions">
<button class="btn-primary">Анонсировать лекцию</button>
<button class="btn-secondary">Посмотреть отзывы</button>
<button class="btn-secondary">Отметить посещение</button>
<button class="btn-primary" @click="router.push('/teacher/lectures')">Мои лекции</button>
<button class="btn-secondary" @click="router.push('/teacher/analytics')">Посмотреть отзывы</button>
</div>
</div>
<div class="stats-row">
<StatsWidget label="Предстоящие лекции" :value="3" icon="📅" color="green" />
<StatsWidget label="Записавшихся" :value="47" icon="👥" color="aqua" />
<StatsWidget label="Средняя оценка" :value="4.6" icon="⭐" color="orange" />
<StatsWidget label="Вовлеченность вне направления" :value="'38%'" icon="🌍" color="purple" />
<StatsWidget label="Предстоящие лекции" :value="upcoming.length" icon="📅" color="green" />
<StatsWidget label="Записавшихся" :value="enrolledTotal" icon="👥" color="aqua" />
<StatsWidget label="Средняя оценка" :value="'—'" icon="⭐" color="orange" />
<StatsWidget label="Вовлеченность вне направления" :value="`${visibility}%`" icon="🌍" color="purple" />
</div>
<GlassCard>
<div class="section-title">Заметность за пределами направления</div>
<div class="visibility">
<div class="visibility-meta">
38% студентов из других институтов · Цель 50%
{{ visibility }}% студентов из других институтов · Цель 50%
</div>
<ProgressBar :value="38" :max="100" />
<ProgressBar :value="visibility" :max="100" />
</div>
</GlassCard>
<GlassCard>
<div class="section-title">Ближайшие открытые лекции</div>
<div class="upcoming">
<EmptyState v-if="!upcoming.length" title="Лекций пока нет" subtitle="После синхронизации или назначения лекции появятся здесь." />
<div v-else class="upcoming">
<div class="upcoming-item" v-for="l in upcoming" :key="l.id">
<div>
<div class="upcoming-title">{{ l.title }}</div>
<div class="upcoming-meta">📅 {{ new Date(l.date).toLocaleDateString('ru-RU') }} · {{ l.time }}</div>
<div class="upcoming-meta">Записалось {{ l.totalSeats - l.freeSeats }} студентов</div>
</div>
<button class="btn-secondary btn-sm">Управлять</button>
<button class="btn-secondary btn-sm" @click="router.push('/teacher/lectures')">Управлять</button>
</div>
</div>
</GlassCard>