feat: перелопатил синхронизацию преподавателей
Backend CI / build-and-test (push) Failing after 13m11s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Failing after 10m12s
Frontend CI / build-and-check (push) Failing after 16m9s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14m6s
🚀 Create and publish a Docker image / Build & publish backend image (push) Failing after 14m58s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Failing after 14m58s
Backend CI / build-and-test (push) Failing after 13m11s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Failing after 10m12s
Frontend CI / build-and-check (push) Failing after 16m9s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14m6s
🚀 Create and publish a Docker image / Build & publish backend image (push) Failing after 14m58s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Failing after 14m58s
This commit is contained in:
@@ -24,15 +24,16 @@ function mapApiRoles(roles: string[] | undefined): UserRole[] {
|
||||
}
|
||||
|
||||
function getDefaultActiveRole(roles: UserRole[]): UserRole {
|
||||
if (roles.includes('student')) return 'student'
|
||||
if (roles.includes('teacher')) return 'teacher'
|
||||
if (roles.includes('admin')) return 'admin'
|
||||
if (roles.includes('teacher')) return 'teacher'
|
||||
if (roles.includes('student')) return 'student'
|
||||
return 'student'
|
||||
}
|
||||
|
||||
export function mapApiUser(user: UserAuthDto | UserDto | CurrentUserDto, stats?: UserStatsDto): User {
|
||||
const roles = mapApiRoles(user.roles)
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.displayName || user.email || 'Пользователь UniVerse',
|
||||
email: user.email || '',
|
||||
roles,
|
||||
@@ -68,6 +69,7 @@ export function mapApiLecture(lecture: LectureDto): Lecture {
|
||||
|
||||
return {
|
||||
id: String(lecture.id),
|
||||
teacherId: lecture.teacherId,
|
||||
title: lecture.title || lecture.courseName || 'Лекция без названия',
|
||||
description: lecture.description || 'Описание появится позже.',
|
||||
teacher: lecture.teacherName || 'Преподаватель уточняется',
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface LoginMicrosoftRequest {
|
||||
}
|
||||
|
||||
export interface UserAuthDto {
|
||||
id: number
|
||||
email: string
|
||||
displayName?: string | null
|
||||
roles: ApiUserRole[]
|
||||
|
||||
@@ -3,6 +3,7 @@ import { computed, ref } from 'vue'
|
||||
import { lecturesApi, usersApi } from '@/api'
|
||||
import { mapApiLecture, mapApiReview } from '@/api/mappers'
|
||||
import type { Lecture, Review } from '@/types'
|
||||
import type { LectureQuery } from '@/api/types'
|
||||
import { useUserStore } from './user'
|
||||
|
||||
export const useLecturesStore = defineStore('lectures', () => {
|
||||
@@ -18,11 +19,11 @@ export const useLecturesStore = defineStore('lectures', () => {
|
||||
lectures.value.filter(l => registered.value.includes(l.id) || l.registered),
|
||||
)
|
||||
|
||||
async function fetchLectures() {
|
||||
async function fetchLectures(query: LectureQuery = {}) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const payload = await lecturesApi.list({ PageSize: 100 })
|
||||
const payload = await lecturesApi.list({ PageSize: 100, ...query })
|
||||
lectures.value = payload.map(mapApiLecture)
|
||||
registered.value = lectures.value.filter(l => l.registered).map(l => l.id)
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export type UserRole = 'student' | 'teacher' | 'admin'
|
||||
|
||||
export interface User {
|
||||
id: number
|
||||
name: string
|
||||
email: string
|
||||
roles: UserRole[]
|
||||
@@ -30,6 +31,7 @@ export interface EnrollmentSlotRule {
|
||||
|
||||
export interface Lecture {
|
||||
id: string
|
||||
teacherId?: number | null
|
||||
title: string
|
||||
description: string
|
||||
teacher: string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import GlassCard from '@/components/ui/GlassCard.vue'
|
||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||
import EmptyState from '@/components/ui/EmptyState.vue'
|
||||
@@ -7,9 +7,11 @@ import { lecturesApi } from '@/api'
|
||||
import type { Review } from '@/types'
|
||||
import { mapApiReview } from '@/api/mappers'
|
||||
import { useLecturesStore } from '@/stores/lectures'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const ratingTrend = [4.2, 4.5, 4.6, 4.8, 4.7]
|
||||
const lecturesStore = useLecturesStore()
|
||||
const auth = useAuthStore()
|
||||
const reviews = ref<Review[]>([])
|
||||
|
||||
const positive = computed(() => reviews.value.filter(r => r.sentiment === 'positive').length)
|
||||
@@ -18,12 +20,16 @@ const negative = computed(() => reviews.value.filter(r => r.sentiment === 'negat
|
||||
const total = computed(() => reviews.value.length || 1)
|
||||
const pct = (value: number) => Math.round((value / total.value) * 100)
|
||||
|
||||
onMounted(async () => {
|
||||
if (!lecturesStore.all.length) await lecturesStore.fetchLectures()
|
||||
async function fetchTeacherAnalytics() {
|
||||
if (!auth.user?.id) return
|
||||
await lecturesStore.fetchLectures({ TeacherId: auth.user.id })
|
||||
const targetLectures = lecturesStore.all.slice(0, 5)
|
||||
const payload = await Promise.allSettled(targetLectures.map(l => lecturesApi.reviews(l.id)))
|
||||
reviews.value = payload.flatMap(result => (result.status === 'fulfilled' ? result.value.map(mapApiReview) : []))
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(fetchTeacherAnalytics)
|
||||
watch(() => auth.user?.id, fetchTeacherAnalytics)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useLecturesStore } from '@/stores/lectures'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
@@ -13,16 +13,19 @@ 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
|
||||
return 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.enrolledSeats, 0))
|
||||
const visibility = computed(() => (teacherLectures.value.length ? Math.min(100, Math.round(enrolledTotal.value * 4)) : 0))
|
||||
|
||||
onMounted(() => {
|
||||
if (!lecturesStore.all.length) void lecturesStore.fetchLectures()
|
||||
})
|
||||
function fetchTeacherLectures() {
|
||||
if (!auth.user?.id) return
|
||||
void lecturesStore.fetchLectures({ TeacherId: auth.user.id })
|
||||
}
|
||||
|
||||
onMounted(fetchTeacherLectures)
|
||||
watch(() => auth.user?.id, fetchTeacherLectures)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useLecturesStore } from '@/stores/lectures'
|
||||
import GlassCard from '@/components/ui/GlassCard.vue'
|
||||
@@ -18,8 +18,7 @@ const columns = [
|
||||
]
|
||||
|
||||
const rows = computed(() => {
|
||||
const owned = lecturesStore.all.filter(l => auth.user && l.teacher.includes(auth.user.name))
|
||||
return (owned.length ? owned : lecturesStore.all).map(l => ({
|
||||
return lecturesStore.all.map(l => ({
|
||||
id: l.id,
|
||||
title: l.title,
|
||||
date: `${new Date(l.date).toLocaleDateString('ru-RU')} ${l.time}`,
|
||||
@@ -28,9 +27,13 @@ const rows = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!lecturesStore.all.length) void lecturesStore.fetchLectures()
|
||||
})
|
||||
function fetchTeacherLectures() {
|
||||
if (!auth.user?.id) return
|
||||
void lecturesStore.fetchLectures({ TeacherId: auth.user.id })
|
||||
}
|
||||
|
||||
onMounted(fetchTeacherLectures)
|
||||
watch(() => auth.user?.id, fetchTeacherLectures)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user