Files
UniVerse/frontend/src/views/student/ReviewFormView.vue
T
serega404 779b6aba77
🚀 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 54s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 27s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 6s
feat: первое подключение фронтенда
2026-05-11 01:33:38 +03:00

115 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import GlassCard from '@/components/ui/GlassCard.vue'
import { reviewsApi } from '@/api'
const route = useRoute()
const rating = ref<'positive' | 'neutral' | 'negative'>('positive')
const text = ref('Лекция была хорошо структурирована, особенно понравились практические примеры и разбор кейсов.')
const submitted = ref(false)
const editing = ref(false)
const loading = ref(false)
const error = ref('')
const ratingMap = {
positive: 'Like',
neutral: 'Neutral',
negative: 'Dislike',
} as const
async function submit() {
loading.value = true
error.value = ''
try {
await reviewsApi.create(String(route.params.id), ratingMap[rating.value], text.value)
submitted.value = true
editing.value = false
} catch (err) {
error.value = err instanceof Error ? err.message : 'Не удалось отправить отзыв.'
} finally {
loading.value = false
}
}
</script>
<template>
<div class="review page-content">
<div class="header">
<div>
<h1 class="page-title">Отзыв о лекции #{{ route.params.id }}</h1>
<p class="text-secondary">Ваш отзыв помогает улучшать качество лекций и приносит бонусные монеты.</p>
</div>
</div>
<GlassCard>
<div v-if="submitted && !editing" class="success-state">
<div class="success-icon"></div>
<div class="success-title">Отзыв отправлен и будет обработан</div>
<div class="success-sub">
Спасибо! Оценка полезности отзыва рассчитывается автоматически. Студентам не показывается техническая оценка LLM.
</div>
<button class="btn-secondary" @click="editing = true">Редактировать отзыв</button>
</div>
<form v-else @submit.prevent="submit" class="form">
<label class="field-label">Ваш отзыв о лекции</label>
<textarea v-model="text" rows="6" placeholder="Опишите, что было полезно, а что можно улучшить"></textarea>
<label class="field-label">Оценка впечатлений</label>
<div class="rating-options">
<button type="button" :class="{ active: rating === 'positive' }" @click="rating = 'positive'">👍 Положительный</button>
<button type="button" :class="{ active: rating === 'neutral' }" @click="rating = 'neutral'">😐 Нейтральный</button>
<button type="button" :class="{ active: rating === 'negative' }" @click="rating = 'negative'">👎 Отрицательный</button>
</div>
<div class="hint">
💡 Постарайтесь быть конкретными: что понравилось, где было сложно, какие темы хотите раскрыть глубже.
</div>
<div class="error" v-if="error">{{ error }}</div>
<div class="form-actions">
<button class="btn-primary" type="submit" :disabled="loading">{{ loading ? 'Отправляем...' : 'Отправить отзыв' }}</button>
<button class="btn-secondary" type="button" :disabled="submitted">Сохранить черновик</button>
</div>
</form>
</GlassCard>
</div>
</template>
<style scoped>
.review { display: flex; flex-direction: column; gap: 16px; }
.form { display: flex; flex-direction: column; gap: 12px; }
.field-label { font-weight: 600; font-size: 13px; color: var(--color-text-secondary); }
textarea {
padding: 12px;
border-radius: var(--radius-sm);
border: 1px solid var(--color-border-glass);
background: rgba(255,255,255,0.8);
font-size: 14px;
resize: vertical;
}
.rating-options { display: flex; gap: 10px; flex-wrap: wrap; }
.rating-options button {
padding: 8px 12px;
border-radius: var(--radius-sm);
border: 1px solid var(--color-border-glass);
background: rgba(255,255,255,0.6);
cursor: pointer;
font-size: 13px;
}
.rating-options button.active {
border-color: var(--color-primary);
background: rgba(34,197,94,0.15);
color: var(--color-primary-dark);
font-weight: 600;
}
.hint { font-size: 12px; color: var(--color-text-secondary); background: rgba(255,255,255,0.6); padding: 8px 12px; border-radius: var(--radius-sm); }
.form-actions { display: flex; gap: 10px; }
.success-state { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.success-icon { font-size: 28px; }
.success-title { font-size: 16px; font-weight: 700; }
.success-sub { font-size: 13px; color: var(--color-text-secondary); }
.error { color: var(--color-error); font-size: 13px; }
</style>