Undangan/proyek-frontend/app/components/undangan/undangan-ulang-tahun-starter.vue

203 lines
6.6 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="relative min-h-screen w-full bg-gradient-to-br from-red-900 via-red-800 to-blue-900 overflow-hidden text-white"
>
<!-- 🕸 ANIMATED SPIDER-WEB BACKGROUND -->
<div class="absolute inset-0 opacity-15 animate-spin-slow">
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" class="w-full h-full">
<g stroke="white" stroke-width="0.4" fill="none">
<circle cx="250" cy="250" r="60" />
<circle cx="250" cy="250" r="120" />
<circle cx="250" cy="250" r="180" />
<circle cx="250" cy="250" r="240" />
<circle cx="250" cy="250" r="300" />
<line x1="250" y1="0" x2="250" y2="500" />
<line x1="0" y1="250" x2="500" y2="250" />
<line x1="125" y1="0" x2="375" y2="500" />
<line x1="375" y1="0" x2="125" y2="500" />
</g>
</svg>
</div>
<!-- 💫 LIGHT ORBS -->
<div class="absolute top-10 left-10 w-32 h-32 bg-red-500/30 rounded-full blur-2xl"></div>
<div class="absolute bottom-20 right-10 w-24 h-24 bg-blue-500/25 rounded-full blur-xl"></div>
<!-- 🕷 FLOATING ICONS -->
<div class="absolute top-16 right-16 text-6xl opacity-20 animate-bounce-slow">🕷</div>
<div class="absolute bottom-20 left-16 text-6xl opacity-20 animate-bounce-slow delay-700">🕸</div>
<!-- 🔻 NAVIGATION -->
<nav
v-if="currentSection !== 'landing'"
class="fixed top-6 left-1/2 transform -translate-x-1/2 z-30 animate-fade-in"
>
<ul
class="flex space-x-2 bg-black/70 backdrop-blur-lg px-6 py-3 rounded-full shadow-lg text-sm font-semibold text-white border border-red-500/60"
>
<li v-for="section in sections" :key="section.key">
<button
@click="switchSection(section.key)"
:class="navClass(section.key)"
class="px-3 py-1 rounded-full transition-all duration-300"
>
{{ section.label }}
</button>
</li>
</ul>
</nav>
<!-- 🎵 MUSIC BUTTON -->
<div class="fixed bottom-6 left-6 z-40" v-if="currentSection !== 'landing'">
<button
@click="toggleMusic"
class="bg-gradient-to-r from-red-600 to-blue-600 hover:from-red-700 hover:to-blue-700 p-4 rounded-full shadow-2xl transition-all duration-300 hover:scale-110 animate-pulse-slow"
>
<span class="text-lg">{{ isPlaying ? '⏸️' : '▶️' }}</span>
</button>
</div>
<!-- 🌆 MAIN CONTENT -->
<main
class="relative z-20 h-screen w-screen flex items-center justify-center transition-all duration-700 ease-in-out overflow-hidden"
>
<transition name="fade" mode="out-in">
<component
:is="currentComponent"
:key="currentSection"
v-bind="componentProps"
@open-invitation="switchSection('introduction')"
@addMessage="addMessage"
/>
</transition>
</main>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useRuntimeConfig } from '#app'
// 🧩 Import Semua Komponen
import Landing from '~/components/templates/UltahStarter/Landing.vue'
import Introduction from '~/components/templates/UltahStarter/Introduction.vue'
import Event from '~/components/templates/UltahStarter/Event.vue'
import Gallery from '~/components/templates/UltahStarter/Gallery.vue'
import ThankYou from '~/components/templates/UltahStarter/ThankYou.vue'
// Props dari induk
const props = defineProps({ data: Object })
// URL backend
const config = useRuntimeConfig()
const backendUrl = config.public.apiBaseUrl
// Data backend
const formData = computed(() => props.data.form || {})
// ✅ Gambar galeri
const galleryImages = computed(() => {
const f = formData.value
return [f.foto_1, f.foto_2, f.foto_3, f.foto_4, f.foto_5]
.filter(Boolean)
.map(img => `${backendUrl}/storage/${img.replace(/^public\//, '')}`)
})
// 🌍 Navigasi section
const currentSection = ref('landing')
const sections = [
{ key: 'introduction', label: 'Intro' },
{ key: 'event', label: 'Event' },
{ key: 'gallery', label: 'Gallery' },
{ key: 'thanks', label: 'Thanks' }
]
const switchSection = (section) => (currentSection.value = section)
// 🔊 Musik
const isPlaying = ref(false)
const toggleMusic = () => (isPlaying.value = !isPlaying.value)
// 💬 Buku tamu
const messages = ref([])
const addMessage = (msg) => messages.value.push(msg)
// 🎭 Komponen dinamis berdasarkan section
const currentComponent = computed(() => {
switch (currentSection.value) {
case 'landing': return Landing
case 'introduction': return Introduction
case 'event': return Event
case 'gallery': return Gallery
case 'guestbook': return GuestBook
case 'thanks': return ThankYou
default: return Landing
}
})
// 🎁 Props untuk tiap komponen
const componentProps = computed(() => ({
childName: formData.value.nama_panggilan,
guestName: props.data.nama_tamu,
age: formData.value.umur_yang_dirayakan,
childOrder: formData.value.anak_ke,
parentsName: `${formData.value.nama_bapak} & ${formData.value.nama_ibu}`,
childPhoto: formData.value.foto?.length
? `${backendUrl}/storage/${formData.value.foto[0]}`
: null,
hari_tanggal_acara: formData.value.hari_tanggal_acara,
waktu: formData.value.waktu,
alamat: formData.value.alamat,
link_gmaps: formData.value.link_gmaps,
hitung_mundur: formData.value.hitung_mundur,
images: galleryImages.value,
messages: messages.value,
jsonData: formData.value
}))
// Style aktif navigasi
const navClass = (key) =>
currentSection.value === key
? 'bg-gradient-to-r from-red-600 to-blue-600 text-white shadow-lg'
: 'hover:text-red-400 hover:bg-black/40'
</script>
<style scoped>
/* 🌟 Animations */
@keyframes spin-slow {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes bounce-slow {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@keyframes pulse-slow {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
/* ✨ Fade transition between sections */
.fade-enter-active, .fade-leave-active {
transition: opacity 0.8s ease, transform 0.8s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
transform: scale(0.98);
}
.animate-spin-slow { animation: spin-slow 40s linear infinite; }
.animate-bounce-slow { animation: bounce-slow 4s ease-in-out infinite; }
.animate-pulse-slow { animation: pulse-slow 3s ease-in-out infinite; }
.animate-fade-in {
animation: fade-in 0.6s ease-out;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>