Files
Portfolio-Game/frontend/app/components/layout/ConsentBanner.vue
skycel 9fd66def12 🎲 Add Pinia progression store & GDPR consent banner (Story 1.6)
Implements useProgressionStore with conditional localStorage persistence
(only after RGPD consent), immersive ConsentBanner with narrator style,
WelcomeBack component for returning visitors, and connects progress bar
in header to store.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 21:00:49 +01:00

74 lines
1.9 KiB
Vue

<template>
<Transition name="consent">
<div
v-if="showBanner"
class="fixed bottom-0 inset-x-0 z-50 p-4 md:p-6 bg-sky-dark/95 backdrop-blur-sm border-t border-sky-text/10"
role="dialog"
:aria-label="$t('consent.aria_label')"
>
<div class="max-w-2xl mx-auto">
<div class="flex items-start gap-4">
<div class="text-3xl shrink-0" aria-hidden="true">&#x1f577;&#xfe0f;</div>
<div class="flex-1">
<p class="font-narrative text-sky-text text-sm md:text-base mb-4">
{{ $t('consent.message') }}
</p>
<div class="flex flex-wrap gap-3">
<button
class="px-6 py-2 bg-sky-accent text-sky-dark font-ui font-semibold rounded-lg hover:opacity-90 transition-opacity"
@click="acceptConsent"
>
{{ $t('consent.accept') }}
</button>
<button
class="px-6 py-2 text-sky-text/70 hover:text-sky-text font-ui transition-colors"
@click="refuseConsent"
>
{{ $t('consent.refuse') }}
</button>
</div>
</div>
</div>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
import { useProgressionStore } from '~/stores/progression'
const store = useProgressionStore()
const showBanner = computed(() => {
return import.meta.client && store.consentGiven === null
})
function acceptConsent() {
store.setConsent(true)
}
function refuseConsent() {
store.setConsent(false)
}
</script>
<style scoped>
.consent-enter-active,
.consent-leave-active {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.consent-enter-from,
.consent-leave-to {
transform: translateY(100%);
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.consent-enter-active,
.consent-leave-active {
transition: none;
}
}
</style>