829 lines
22 KiB
Vue
829 lines
22 KiB
Vue
<template>
|
|
<div class="vocabulary-practice">
|
|
<!-- 練習設定面板 -->
|
|
<div v-if="!currentSession" class="practice-setup">
|
|
<div class="setup-header">
|
|
<h1 class="page-title">詞彙練習</h1>
|
|
<p class="page-subtitle">選擇練習類型和難度開始學習</p>
|
|
|
|
<!-- 多標籤頁狀態指示器 -->
|
|
<div v-if="activeTabs.size > 1" class="multi-tab-status">
|
|
<q-chip
|
|
color="info"
|
|
text-color="white"
|
|
icon="tab"
|
|
:label="`${activeTabs.size} 個學習標籤頁`"
|
|
/>
|
|
<q-tooltip>偵測到多個學習標籤頁,進度將自動同步</q-tooltip>
|
|
</div>
|
|
</div>
|
|
|
|
<q-card class="setup-card">
|
|
<q-card-section>
|
|
<div class="setup-section">
|
|
<h3 class="section-title">練習類型</h3>
|
|
<q-option-group
|
|
v-model="selectedExerciseType"
|
|
:options="exerciseTypeOptions"
|
|
color="primary"
|
|
inline
|
|
/>
|
|
</div>
|
|
|
|
<q-separator class="q-my-md" />
|
|
|
|
<div class="setup-section">
|
|
<h3 class="section-title">難度等級</h3>
|
|
<q-option-group
|
|
v-model="selectedDifficulty"
|
|
:options="difficultyOptions"
|
|
color="primary"
|
|
type="checkbox"
|
|
inline
|
|
/>
|
|
</div>
|
|
|
|
<q-separator class="q-my-md" />
|
|
|
|
<div class="setup-section">
|
|
<h3 class="section-title">練習設定</h3>
|
|
<div class="settings-grid">
|
|
<q-input
|
|
v-model.number="questionCount"
|
|
label="題目數量"
|
|
type="number"
|
|
:min="5"
|
|
:max="50"
|
|
outlined
|
|
dense
|
|
/>
|
|
|
|
<q-toggle
|
|
v-model="enableAudio"
|
|
label="啟用音頻"
|
|
color="primary"
|
|
/>
|
|
|
|
<q-toggle
|
|
v-model="enableHints"
|
|
label="啟用提示"
|
|
color="primary"
|
|
/>
|
|
|
|
<q-toggle
|
|
v-model="shuffleOptions"
|
|
label="選項隨機排序"
|
|
color="primary"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
|
|
<q-card-actions align="right" class="q-pa-md">
|
|
<q-btn
|
|
color="primary"
|
|
size="lg"
|
|
@click="startPractice"
|
|
:loading="vocabularyStore.isLoading"
|
|
:disable="selectedDifficulty.length === 0"
|
|
>
|
|
<q-icon name="play_arrow" class="q-mr-sm" />
|
|
開始練習
|
|
</q-btn>
|
|
</q-card-actions>
|
|
</q-card>
|
|
|
|
<!-- 統計資訊 -->
|
|
<div class="stats-section">
|
|
<q-card flat class="stat-card">
|
|
<q-card-section>
|
|
<div class="stat-item">
|
|
<q-icon name="book" size="md" color="primary" />
|
|
<div>
|
|
<div class="stat-value">{{ vocabularyStore.vocabularies.length }}</div>
|
|
<div class="stat-label">總詞彙數</div>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
</q-card>
|
|
|
|
<q-card flat class="stat-card">
|
|
<q-card-section>
|
|
<div class="stat-item">
|
|
<q-icon name="trending_up" size="md" color="green" />
|
|
<div>
|
|
<div class="stat-value">{{ vocabularyStore.masteredWords.length }}</div>
|
|
<div class="stat-label">已掌握</div>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
</q-card>
|
|
|
|
<q-card flat class="stat-card">
|
|
<q-card-section>
|
|
<div class="stat-item">
|
|
<q-icon name="schedule" size="md" color="orange" />
|
|
<div>
|
|
<div class="stat-value">{{ vocabularyStore.wordsForReview.length }}</div>
|
|
<div class="stat-label">待複習</div>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
</q-card>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 練習進行中 -->
|
|
<div v-else class="practice-session">
|
|
<!-- 進度條 -->
|
|
<div class="session-header">
|
|
<div class="progress-info">
|
|
<div class="progress-text">
|
|
{{ currentSession.completed_questions }} / {{ currentSession.total_questions }}
|
|
</div>
|
|
<div class="accuracy-text">
|
|
準確率: {{ Math.round(vocabularyStore.sessionAccuracy) }}%
|
|
</div>
|
|
</div>
|
|
<q-linear-progress
|
|
:value="vocabularyStore.sessionProgress / 100"
|
|
color="primary"
|
|
size="8px"
|
|
rounded
|
|
/>
|
|
</div>
|
|
|
|
<!-- 當前題目 -->
|
|
<div v-if="currentExercise" class="exercise-container">
|
|
<q-card class="exercise-card">
|
|
<q-card-section>
|
|
<!-- 題目 -->
|
|
<div class="question-section">
|
|
<h2 class="question-text">{{ currentExercise.question }}</h2>
|
|
|
|
<!-- 詞彙資訊 -->
|
|
<div v-if="currentVocabulary" class="vocabulary-info">
|
|
<div class="word-display">
|
|
<span class="word">{{ currentVocabulary.word }}</span>
|
|
<span class="phonetic">{{ currentVocabulary.phonetic }}</span>
|
|
<q-btn
|
|
v-if="enableAudio && currentVocabulary.audio_url"
|
|
flat
|
|
round
|
|
dense
|
|
icon="volume_up"
|
|
@click="playAudio"
|
|
class="audio-btn"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 選項 -->
|
|
<div class="options-section">
|
|
<q-option-group
|
|
v-model="selectedAnswer"
|
|
:options="displayOptions"
|
|
color="primary"
|
|
type="radio"
|
|
@update:model-value="onAnswerSelect"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 提示 -->
|
|
<div v-if="showHint && enableHints" class="hint-section">
|
|
<q-banner class="hint-banner" icon="lightbulb">
|
|
<template v-slot:action>
|
|
<q-btn flat round dense icon="close" @click="showHint = false" />
|
|
</template>
|
|
{{ currentExercise.explanation || '這是一個提示...' }}
|
|
</q-banner>
|
|
</div>
|
|
</q-card-section>
|
|
|
|
<q-card-actions align="between" class="q-pa-md">
|
|
<div>
|
|
<q-btn
|
|
v-if="enableHints && !showHint"
|
|
flat
|
|
icon="help"
|
|
label="提示"
|
|
@click="showHint = true"
|
|
/>
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<q-btn
|
|
flat
|
|
label="跳過"
|
|
@click="skipQuestion"
|
|
class="q-mr-md"
|
|
/>
|
|
<q-btn
|
|
color="primary"
|
|
label="確認"
|
|
@click="submitAnswer"
|
|
:disable="!selectedAnswer"
|
|
:loading="isSubmitting"
|
|
/>
|
|
</div>
|
|
</q-card-actions>
|
|
</q-card>
|
|
|
|
<!-- 即時反饋 -->
|
|
<div v-if="showFeedback" class="feedback-section">
|
|
<q-card :class="['feedback-card', lastAnswerCorrect ? 'correct' : 'incorrect']">
|
|
<q-card-section>
|
|
<div class="feedback-content">
|
|
<q-icon
|
|
:name="lastAnswerCorrect ? 'check_circle' : 'cancel'"
|
|
size="xl"
|
|
:color="lastAnswerCorrect ? 'green' : 'red'"
|
|
/>
|
|
<div>
|
|
<div class="feedback-title">
|
|
{{ lastAnswerCorrect ? '答對了!' : '答錯了' }}
|
|
</div>
|
|
<div v-if="!lastAnswerCorrect && correctAnswerText" class="correct-answer">
|
|
正確答案:{{ correctAnswerText }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
</q-card>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 練習完成 -->
|
|
<div v-if="sessionCompleted" class="session-complete">
|
|
<q-card class="completion-card">
|
|
<q-card-section class="text-center">
|
|
<q-icon name="celebration" size="4rem" color="primary" />
|
|
<h2>練習完成!</h2>
|
|
|
|
<div class="completion-stats">
|
|
<div class="stat-row">
|
|
<span>總題數:</span>
|
|
<span>{{ currentSession.total_questions }}</span>
|
|
</div>
|
|
<div class="stat-row">
|
|
<span>答對:</span>
|
|
<span class="correct">{{ currentSession.correct_answers }}</span>
|
|
</div>
|
|
<div class="stat-row">
|
|
<span>答錯:</span>
|
|
<span class="incorrect">{{ currentSession.incorrect_answers }}</span>
|
|
</div>
|
|
<div class="stat-row">
|
|
<span>準確率:</span>
|
|
<span>{{ Math.round(vocabularyStore.sessionAccuracy) }}%</span>
|
|
</div>
|
|
<div class="stat-row">
|
|
<span>平均時間:</span>
|
|
<span>{{ Math.round(currentSession.average_response_time / 1000) }}秒</span>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
|
|
<q-card-actions align="center">
|
|
<q-btn
|
|
color="primary"
|
|
label="查看詳細結果"
|
|
@click="goToResults"
|
|
class="q-mr-md"
|
|
/>
|
|
<q-btn
|
|
outline
|
|
color="primary"
|
|
label="再次練習"
|
|
@click="restartPractice"
|
|
/>
|
|
</q-card-actions>
|
|
</q-card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useVocabularyStore } from '@/stores/vocabulary'
|
|
import { useMultiTabLearning } from '@/composables/useMultiTabLearning'
|
|
import { useQuasar } from 'quasar'
|
|
import type { ExerciseType } from '@/types/vocabulary'
|
|
|
|
const router = useRouter()
|
|
const vocabularyStore = useVocabularyStore()
|
|
const $q = useQuasar()
|
|
|
|
// 多標籤頁學習支援
|
|
const {
|
|
currentTabId,
|
|
activeTabs,
|
|
isSyncing,
|
|
syncConflicts,
|
|
startMultiTabSession,
|
|
resolveConflict
|
|
} = useMultiTabLearning()
|
|
|
|
// 響應式數據
|
|
const selectedExerciseType = ref<ExerciseType>('multiple_choice_definition')
|
|
const selectedDifficulty = ref<number[]>([1, 2, 3])
|
|
const questionCount = ref(10)
|
|
const enableAudio = ref(true)
|
|
const enableHints = ref(true)
|
|
const shuffleOptions = ref(true)
|
|
|
|
const selectedAnswer = ref<string | null>(null)
|
|
const showHint = ref(false)
|
|
const showFeedback = ref(false)
|
|
const lastAnswerCorrect = ref(false)
|
|
const correctAnswerText = ref('')
|
|
const isSubmitting = ref(false)
|
|
const sessionCompleted = ref(false)
|
|
const questionStartTime = ref<number>(0)
|
|
|
|
// 練習類型選項
|
|
const exerciseTypeOptions = [
|
|
{ label: '詞義選擇', value: 'multiple_choice_definition' },
|
|
{ label: '翻譯選擇', value: 'multiple_choice_translation' },
|
|
{ label: '同義詞選擇', value: 'multiple_choice_synonym' }
|
|
]
|
|
|
|
// 難度選項
|
|
const difficultyOptions = [
|
|
{ label: '基礎 (1)', value: 1 },
|
|
{ label: '初級 (2)', value: 2 },
|
|
{ label: '中級 (3)', value: 3 },
|
|
{ label: '高級 (4)', value: 4 },
|
|
{ label: '專家 (5)', value: 5 }
|
|
]
|
|
|
|
// 計算屬性
|
|
const currentSession = computed(() => vocabularyStore.currentSession)
|
|
const currentExercises = computed(() => vocabularyStore.currentExercises)
|
|
const currentExercise = computed(() => {
|
|
if (!currentSession.value || !currentExercises.value.length) return null
|
|
const index = currentSession.value.completed_questions
|
|
return currentExercises.value[index] || null
|
|
})
|
|
|
|
const currentVocabulary = computed(() => {
|
|
if (!currentExercise.value) return null
|
|
return vocabularyStore.vocabularies.find(v => v.id === currentExercise.value!.vocabulary_id)
|
|
})
|
|
|
|
const displayOptions = computed(() => {
|
|
if (!currentExercise.value) return []
|
|
|
|
let options = [...currentExercise.value.options]
|
|
if (shuffleOptions.value) {
|
|
// 簡單的洗牌算法
|
|
for (let i = options.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1))
|
|
;[options[i], options[j]] = [options[j], options[i]]
|
|
}
|
|
}
|
|
|
|
return options.map(opt => ({
|
|
label: opt.text,
|
|
value: opt.id
|
|
}))
|
|
})
|
|
|
|
// 方法
|
|
const startPractice = async () => {
|
|
try {
|
|
// 檢查是否有同步衝突
|
|
if (syncConflicts.value.length > 0) {
|
|
const result = await showConflictDialog()
|
|
if (!result) return
|
|
}
|
|
|
|
// 更新練習設定
|
|
vocabularyStore.updatePracticeSettings({
|
|
exercise_type: selectedExerciseType.value,
|
|
difficulty_levels: selectedDifficulty.value,
|
|
question_count: questionCount.value,
|
|
enable_audio: enableAudio.value,
|
|
enable_hints: enableHints.value,
|
|
shuffle_options: shuffleOptions.value
|
|
})
|
|
|
|
// 載入詞彙數據
|
|
await vocabularyStore.fetchVocabularies({
|
|
difficulty: selectedDifficulty.value,
|
|
limit: questionCount.value
|
|
})
|
|
|
|
// 開始多標籤頁會話
|
|
const vocabularyIds = vocabularyStore.vocabularies
|
|
.slice(0, questionCount.value)
|
|
.map(v => v.id)
|
|
|
|
await startMultiTabSession(vocabularyIds, selectedExerciseType.value)
|
|
|
|
// 重置狀態
|
|
resetQuestionState()
|
|
questionStartTime.value = Date.now()
|
|
|
|
// 通知用戶多標籤頁功能
|
|
if (activeTabs.value.size > 1) {
|
|
$q.notify({
|
|
message: `已偵測到 ${activeTabs.value.size} 個活躍學習標籤頁,學習進度將自動同步`,
|
|
icon: 'sync',
|
|
color: 'info',
|
|
position: 'top',
|
|
timeout: 3000
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error('開始練習失敗:', error)
|
|
$q.notify({
|
|
message: '開始練習失敗,請重試',
|
|
color: 'negative',
|
|
icon: 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
const onAnswerSelect = (value: string) => {
|
|
// 記錄答案選擇時間
|
|
if (!questionStartTime.value) {
|
|
questionStartTime.value = Date.now()
|
|
}
|
|
}
|
|
|
|
const submitAnswer = async () => {
|
|
if (!currentExercise.value || !selectedAnswer.value) return
|
|
|
|
isSubmitting.value = true
|
|
const responseTime = Date.now() - questionStartTime.value
|
|
|
|
try {
|
|
await vocabularyStore.submitAnswer(
|
|
currentExercise.value.id,
|
|
selectedAnswer.value,
|
|
responseTime
|
|
)
|
|
|
|
// 顯示反饋
|
|
const option = currentExercise.value.options.find(opt => opt.id === selectedAnswer.value)
|
|
lastAnswerCorrect.value = option?.is_correct || false
|
|
|
|
if (!lastAnswerCorrect.value) {
|
|
const correctOption = currentExercise.value.options.find(opt => opt.is_correct)
|
|
correctAnswerText.value = correctOption?.text || ''
|
|
}
|
|
|
|
showFeedback.value = true
|
|
|
|
// 延遲後進入下一題
|
|
setTimeout(() => {
|
|
if (currentSession.value?.status === 'completed') {
|
|
sessionCompleted.value = true
|
|
} else {
|
|
nextQuestion()
|
|
}
|
|
}, 2000)
|
|
|
|
} catch (error) {
|
|
console.error('提交答案失敗:', error)
|
|
} finally {
|
|
isSubmitting.value = false
|
|
}
|
|
}
|
|
|
|
const skipQuestion = () => {
|
|
if (!currentSession.value) return
|
|
|
|
currentSession.value.completed_questions++
|
|
currentSession.value.skipped_questions++
|
|
|
|
if (currentSession.value.completed_questions >= currentSession.value.total_questions) {
|
|
sessionCompleted.value = true
|
|
vocabularyStore.completeSession()
|
|
} else {
|
|
nextQuestion()
|
|
}
|
|
}
|
|
|
|
const nextQuestion = () => {
|
|
resetQuestionState()
|
|
questionStartTime.value = Date.now()
|
|
}
|
|
|
|
const resetQuestionState = () => {
|
|
selectedAnswer.value = null
|
|
showHint.value = false
|
|
showFeedback.value = false
|
|
lastAnswerCorrect.value = false
|
|
correctAnswerText.value = ''
|
|
}
|
|
|
|
const playAudio = () => {
|
|
if (!currentVocabulary.value?.audio_url) return
|
|
|
|
const audio = new Audio(currentVocabulary.value.audio_url)
|
|
audio.play().catch(error => {
|
|
console.error('音頻播放失敗:', error)
|
|
})
|
|
}
|
|
|
|
const goToResults = () => {
|
|
router.push('/learning/vocabulary/results')
|
|
}
|
|
|
|
const restartPractice = () => {
|
|
vocabularyStore.resetCurrentSession()
|
|
sessionCompleted.value = false
|
|
}
|
|
|
|
// 多標籤頁衝突處理
|
|
const showConflictDialog = (): Promise<boolean> => {
|
|
return new Promise((resolve) => {
|
|
$q.dialog({
|
|
title: '多標籤頁學習偵測',
|
|
message: `已偵測到其他標籤頁正在進行相同類型的練習。請選擇處理方式:`,
|
|
options: {
|
|
type: 'radio',
|
|
model: 'merge',
|
|
items: [
|
|
{ label: '合併進度 (推薦)', value: 'merge' },
|
|
{ label: '覆蓋其他標籤頁', value: 'override' },
|
|
{ label: '取消此次練習', value: 'cancel' }
|
|
]
|
|
},
|
|
cancel: true,
|
|
persistent: true
|
|
}).onOk((data) => {
|
|
resolveConflict(data as 'merge' | 'override' | 'cancel')
|
|
resolve(data !== 'cancel')
|
|
}).onCancel(() => {
|
|
resolve(false)
|
|
})
|
|
})
|
|
}
|
|
|
|
// 生命週期
|
|
onMounted(async () => {
|
|
await vocabularyStore.fetchVocabularies()
|
|
})
|
|
|
|
// 監聽器
|
|
watch(currentSession, (newSession) => {
|
|
if (newSession?.status === 'completed') {
|
|
sessionCompleted.value = true
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.vocabulary-practice {
|
|
padding: $space-6;
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.practice-setup {
|
|
.setup-header {
|
|
text-align: center;
|
|
margin-bottom: $space-8;
|
|
|
|
.page-title {
|
|
font-size: $text-3xl;
|
|
font-weight: 700;
|
|
color: $text-primary;
|
|
margin-bottom: $space-2;
|
|
}
|
|
|
|
.page-subtitle {
|
|
font-size: $text-lg;
|
|
color: $text-secondary;
|
|
}
|
|
}
|
|
|
|
.setup-card {
|
|
margin-bottom: $space-6;
|
|
|
|
.setup-section {
|
|
.section-title {
|
|
font-size: $text-lg;
|
|
font-weight: 600;
|
|
margin-bottom: $space-4;
|
|
color: $text-primary;
|
|
}
|
|
}
|
|
|
|
.settings-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: $space-4;
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
.stats-section {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: $space-4;
|
|
|
|
.stat-card {
|
|
background: $card-background;
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $space-3;
|
|
|
|
.stat-value {
|
|
font-size: $text-2xl;
|
|
font-weight: 700;
|
|
color: $text-primary;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: $text-sm;
|
|
color: $text-secondary;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.practice-session {
|
|
.session-header {
|
|
margin-bottom: $space-6;
|
|
|
|
.progress-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: $space-2;
|
|
|
|
.progress-text {
|
|
font-size: $text-lg;
|
|
font-weight: 600;
|
|
color: $text-primary;
|
|
}
|
|
|
|
.accuracy-text {
|
|
font-size: $text-base;
|
|
color: $text-secondary;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.exercise-container {
|
|
.exercise-card {
|
|
margin-bottom: $space-4;
|
|
|
|
.question-section {
|
|
margin-bottom: $space-6;
|
|
|
|
.question-text {
|
|
font-size: $text-xl;
|
|
font-weight: 600;
|
|
color: $text-primary;
|
|
margin-bottom: $space-4;
|
|
}
|
|
|
|
.vocabulary-info {
|
|
.word-display {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $space-3;
|
|
padding: $space-4;
|
|
background: rgba($primary-teal, 0.1);
|
|
border-radius: $radius-lg;
|
|
|
|
.word {
|
|
font-size: $text-2xl;
|
|
font-weight: 700;
|
|
color: $primary-teal;
|
|
}
|
|
|
|
.phonetic {
|
|
font-size: $text-lg;
|
|
color: $text-secondary;
|
|
font-style: italic;
|
|
}
|
|
|
|
.audio-btn {
|
|
color: $primary-teal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.options-section {
|
|
:deep(.q-radio) {
|
|
margin-bottom: $space-3;
|
|
padding: $space-3;
|
|
border-radius: $radius-md;
|
|
transition: background-color 0.2s;
|
|
|
|
&:hover {
|
|
background: rgba($primary-teal, 0.05);
|
|
}
|
|
}
|
|
}
|
|
|
|
.hint-section {
|
|
margin-top: $space-4;
|
|
|
|
.hint-banner {
|
|
background: rgba($warning-orange, 0.1);
|
|
color: $warning-orange;
|
|
}
|
|
}
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: $space-2;
|
|
}
|
|
}
|
|
}
|
|
|
|
.feedback-section {
|
|
.feedback-card {
|
|
&.correct {
|
|
border-left: 4px solid $success-green;
|
|
background: rgba($success-green, 0.1);
|
|
}
|
|
|
|
&.incorrect {
|
|
border-left: 4px solid $error-red;
|
|
background: rgba($error-red, 0.1);
|
|
}
|
|
|
|
.feedback-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $space-3;
|
|
|
|
.feedback-title {
|
|
font-size: $text-lg;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.correct-answer {
|
|
font-size: $text-base;
|
|
color: $text-secondary;
|
|
margin-top: $space-1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.session-complete {
|
|
text-align: center;
|
|
|
|
.completion-card {
|
|
max-width: 500px;
|
|
margin: 0 auto;
|
|
|
|
.completion-stats {
|
|
margin: $space-6 0;
|
|
|
|
.stat-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: $space-2 0;
|
|
border-bottom: 1px solid $divider;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
font-weight: 600;
|
|
font-size: $text-lg;
|
|
}
|
|
|
|
.correct {
|
|
color: $success-green;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.incorrect {
|
|
color: $error-red;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.vocabulary-practice {
|
|
padding: $space-4;
|
|
}
|
|
|
|
.settings-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.stats-section {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style> |