feat: подготовил дизайн (изменения из другого репозитория)
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 5s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 8s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 3s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 5s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 8s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 3s
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import GlassCard from '@/components/ui/GlassCard.vue'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
|
||||
const ratingTrend = [4.2, 4.5, 4.6, 4.8, 4.7]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="teacher-analytics page-content">
|
||||
<h1 class="page-title">Аналитика преподавателя</h1>
|
||||
|
||||
<div class="grid">
|
||||
<GlassCard>
|
||||
<div class="section-title">Динамика оценок</div>
|
||||
<div class="chart">
|
||||
<div v-for="(value, i) in ratingTrend" :key="i" class="bar">
|
||||
<div class="bar-fill" :style="{ height: `${value * 18}px` }"></div>
|
||||
<span class="bar-label">Нед {{ i + 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="avg">Средняя оценка: 4.6</div>
|
||||
</GlassCard>
|
||||
|
||||
<GlassCard>
|
||||
<div class="section-title">Sentiment-анализ отзывов</div>
|
||||
<div class="sentiment">
|
||||
<div>
|
||||
<div class="sentiment-label">Позитивные 65%</div>
|
||||
<ProgressBar :value="65" :max="100" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="sentiment-label">Нейтральные 25%</div>
|
||||
<ProgressBar :value="25" :max="100" color="linear-gradient(90deg, #7DD3FC, #BAE6FD)" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="sentiment-label">Негативные 10%</div>
|
||||
<ProgressBar :value="10" :max="100" color="linear-gradient(90deg, #FCA5A5, #FECACA)" />
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
</div>
|
||||
|
||||
<GlassCard>
|
||||
<div class="section-title">LLM-сводка проблем и рекомендаций</div>
|
||||
<p class="summary">
|
||||
Студенты отмечают сильную практическую часть, но хотят больше разборов вопросов из аудитории.
|
||||
Рекомендуется добавить блок с кейсами из реальных проектов и увеличить время на интерактив.
|
||||
</p>
|
||||
<div class="tags">
|
||||
<span class="tag-chip">много практики</span>
|
||||
<span class="tag-chip">понятные примеры</span>
|
||||
<span class="tag-chip">сложный материал</span>
|
||||
<span class="tag-chip">нужны задания</span>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<GlassCard>
|
||||
<div class="section-title">Анонимные отзывы</div>
|
||||
<div class="reviews">
|
||||
<div class="review">
|
||||
«Больше кейсов и примеров из реальной жизни, лекция очень понравилась»
|
||||
</div>
|
||||
<div class="review">
|
||||
«Темп быстрый, но структура отличная. Хотелось бы больше практических заданий.»
|
||||
</div>
|
||||
<div class="review">
|
||||
«Отличные слайды и примеры, спасибо за доступное объяснение сложных тем.»
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-title">Топ полезных отзывов</div>
|
||||
<ul class="top-list">
|
||||
<li>«Лабораторная часть помогла понять алгоритмы, пожалуйста, добавьте еще 15 минут»</li>
|
||||
<li>«Понравились интерактивные задания, хочется больше времени на Q&A»</li>
|
||||
</ul>
|
||||
</GlassCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.teacher-analytics { display: flex; flex-direction: column; gap: 18px; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
|
||||
.chart { display: flex; gap: 12px; align-items: flex-end; height: 160px; padding: 10px 0; }
|
||||
.bar { display: flex; flex-direction: column; align-items: center; gap: 6px; }
|
||||
.bar-fill { width: 26px; border-radius: 6px 6px 0 0; background: linear-gradient(180deg, #22C55E, #86EFAC); }
|
||||
.bar-label { font-size: 11px; color: var(--color-text-secondary); }
|
||||
.avg { margin-top: 6px; font-weight: 600; }
|
||||
.sentiment { display: flex; flex-direction: column; gap: 12px; }
|
||||
.sentiment-label { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 4px; }
|
||||
.summary { font-size: 14px; color: var(--color-text-secondary); line-height: 1.5; }
|
||||
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
|
||||
.reviews { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
|
||||
.review { background: rgba(255,255,255,0.6); border: 1px solid var(--color-border-glass); padding: 10px; border-radius: var(--radius-sm); font-size: 13px; }
|
||||
.top-list { padding-left: 18px; color: var(--color-text-secondary); font-size: 13px; }
|
||||
</style>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useLecturesStore } from '@/stores/lectures'
|
||||
import GlassCard from '@/components/ui/GlassCard.vue'
|
||||
import StatsWidget from '@/components/ui/StatsWidget.vue'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
|
||||
const lecturesStore = useLecturesStore()
|
||||
const upcoming = computed(() => lecturesStore.all.slice(0, 3))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="teacher-dashboard page-content">
|
||||
<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>
|
||||
</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" />
|
||||
</div>
|
||||
|
||||
<GlassCard>
|
||||
<div class="section-title">Заметность за пределами направления</div>
|
||||
<div class="visibility">
|
||||
<div class="visibility-meta">
|
||||
38% студентов из других институтов · Цель 50%
|
||||
</div>
|
||||
<ProgressBar :value="38" :max="100" />
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<GlassCard>
|
||||
<div class="section-title">Ближайшие открытые лекции</div>
|
||||
<div 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>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.teacher-dashboard { display: flex; flex-direction: column; gap: 18px; }
|
||||
.header { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
||||
.actions { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; }
|
||||
.visibility { display: flex; flex-direction: column; gap: 8px; }
|
||||
.visibility-meta { font-size: 13px; color: var(--color-text-secondary); }
|
||||
.upcoming { display: flex; flex-direction: column; gap: 12px; margin-top: 10px; }
|
||||
.upcoming-item { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--color-border-glass); }
|
||||
.upcoming-item:last-child { border-bottom: none; padding-bottom: 0; }
|
||||
.upcoming-title { font-weight: 700; }
|
||||
.upcoming-meta { font-size: 13px; color: var(--color-text-secondary); }
|
||||
.btn-sm { padding: 6px 12px; font-size: 12px; }
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import GlassCard from '@/components/ui/GlassCard.vue'
|
||||
import DataTable from '@/components/ui/DataTable.vue'
|
||||
import StatusBadge from '@/components/ui/StatusBadge.vue'
|
||||
|
||||
const columns = [
|
||||
{ key: 'title', label: 'Лекция' },
|
||||
{ key: 'date', label: 'Дата' },
|
||||
{ key: 'status', label: 'Статус', align: 'center' },
|
||||
{ key: 'stats', label: 'Записи/Посещения/Отзывы', align: 'center' },
|
||||
{ key: 'actions', label: 'Действия', align: 'right' },
|
||||
]
|
||||
|
||||
const rows = [
|
||||
{ id: '1', title: 'Введение в нейронные сети', date: '07.05 · 14:00', status: 'upcoming', stats: '28 / — / —' },
|
||||
{ id: '2', title: 'Алгоритмы глубокого обучения', date: '08.05 · 16:00', status: 'ongoing', stats: '31 / 22 / 15' },
|
||||
{ id: '3', title: 'Практика по ML в бизнесе', date: '01.05 · 12:00', status: 'completed', stats: '45 / 39 / 27' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="teacher-lectures page-content">
|
||||
<div class="header">
|
||||
<h1 class="page-title">Мои лекции</h1>
|
||||
<button class="btn-primary">Создать лекцию</button>
|
||||
</div>
|
||||
|
||||
<GlassCard>
|
||||
<DataTable :columns="columns" :rows="rows">
|
||||
<template #status="{ value }">
|
||||
<StatusBadge :status="value" />
|
||||
</template>
|
||||
<template #actions>
|
||||
<div class="actions">
|
||||
<button class="btn-ghost">Редактировать</button>
|
||||
<button class="btn-ghost">Открыть/закрыть запись</button>
|
||||
<button class="btn-ghost">Список записавшихся</button>
|
||||
<button class="btn-ghost">Отметить посещение</button>
|
||||
</div>
|
||||
</template>
|
||||
</DataTable>
|
||||
</GlassCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.teacher-lectures { display: flex; flex-direction: column; gap: 16px; }
|
||||
.header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
||||
.actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user