feat(frontend): quiz bonus post-contact (Story 4.9)

- Add BonusQuiz.vue component with 7 randomized questions
- Add challenge-bonus.vue page with intro, quiz, and results
- Redirect to bonus quiz after successful contact form submission
- Add i18n translations for bonus.* (fr/en)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 13:42:30 +01:00
parent 7e87a341a2
commit 065e7a0b6a
7 changed files with 520 additions and 37 deletions

View File

@@ -0,0 +1,175 @@
<template>
<div class="bonus-page min-h-screen bg-sky-dark flex flex-col items-center justify-center p-8 relative">
<!-- Bouton quitter (toujours visible) -->
<button
type="button"
class="absolute top-4 right-4 text-sky-text/60 hover:text-sky-text text-sm font-ui flex items-center gap-2 z-10"
@click="goHome"
>
<span>{{ $t('bonus.exit') }}</span>
<span aria-hidden="true"></span>
</button>
<!-- Intro -->
<Transition name="fade" mode="out-in">
<div
v-if="showIntro"
key="intro"
class="max-w-lg text-center"
>
<img
src="/images/bug/bug-stage-5.svg"
alt="Le Bug"
class="w-24 h-24 mx-auto mb-6"
/>
<h1 class="text-2xl font-ui font-bold text-sky-text mb-4">
{{ $t('bonus.waitingTitle') }}
</h1>
<p class="font-narrative text-lg text-sky-text/60 mb-8">
{{ $t('bonus.waitingMessage') }}
</p>
<div class="space-y-4">
<button
type="button"
class="w-full px-8 py-4 bg-sky-accent text-white font-ui font-semibold rounded-lg hover:bg-sky-accent/90 transition-colors"
@click="startQuiz"
>
{{ $t('bonus.playQuiz') }}
</button>
<button
type="button"
class="w-full px-8 py-4 border border-sky-dark-100 text-sky-text font-ui rounded-lg hover:bg-sky-dark-50 transition-colors"
@click="goHome"
>
{{ $t('bonus.noThanks') }}
</button>
</div>
</div>
<!-- Quiz -->
<div
v-else-if="showQuiz"
key="quiz"
class="w-full max-w-2xl"
>
<FeatureBonusQuiz @complete="handleQuizComplete" />
</div>
<!-- Résultat -->
<div
v-else-if="showResult"
key="result"
class="max-w-lg text-center"
>
<div class="text-6xl mb-4" aria-hidden="true">
{{ getResultEmoji }}
</div>
<h2 class="text-2xl font-ui font-bold text-sky-text mb-2">
{{ $t('bonus.resultTitle') }}
</h2>
<p class="text-4xl font-ui font-bold text-sky-accent mb-4">
{{ score }} / 5
</p>
<p class="font-narrative text-lg text-sky-text/60 mb-8">
{{ getResultMessage }}
</p>
<div class="space-y-4">
<button
type="button"
class="w-full px-8 py-4 bg-sky-accent text-white font-ui font-semibold rounded-lg hover:bg-sky-accent/90 transition-colors"
@click="playAgain"
>
{{ $t('bonus.playAgain') }}
</button>
<button
type="button"
class="w-full px-8 py-4 border border-sky-dark-100 text-sky-text font-ui rounded-lg hover:bg-sky-dark-50 transition-colors"
@click="goHome"
>
{{ $t('bonus.backHome') }}
</button>
</div>
<!-- Confirmation message envoyé -->
<p class="mt-8 text-sm text-sky-text/60">
{{ $t('bonus.messageConfirm') }}
</p>
</div>
</Transition>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'adventure',
})
const { t } = useI18n()
const router = useRouter()
const { setPageMeta } = useSeo()
setPageMeta({
title: t('bonus.pageTitle'),
description: t('bonus.pageDescription'),
})
// États
const showIntro = ref(true)
const showQuiz = ref(false)
const showResult = ref(false)
const score = ref(0)
function startQuiz() {
showIntro.value = false
showQuiz.value = true
}
function handleQuizComplete(finalScore: number) {
score.value = finalScore
showQuiz.value = false
showResult.value = true
}
function playAgain() {
showResult.value = false
showIntro.value = true
score.value = 0
}
function goHome() {
router.push('/')
}
const getResultEmoji = computed(() => {
if (score.value === 5) return '🏆'
if (score.value >= 3) return '🎉'
return '💪'
})
const getResultMessage = computed(() => {
if (score.value === 5) return t('bonus.perfectMessage')
if (score.value >= 3) return t('bonus.goodMessage')
return t('bonus.tryMessage')
})
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>