nama tamu

This commit is contained in:
Farhaan4 2025-10-27 15:57:43 +07:00
parent bda7f996f4
commit f76c9a4987
6 changed files with 131 additions and 109 deletions

View File

@ -14,7 +14,7 @@
<!-- Right Side (Text) --> <!-- Right Side (Text) -->
<div class="flex-1 text-center lg:text-left space-y-6 z-10 lg:pl-10"> <div class="flex-1 text-center lg:text-left space-y-6 z-10 lg:pl-10">
<h1 class="text-orange-700 text-xl md:text-2xl font-semibold"> <h1 class="text-orange-700 text-xl md:text-2xl font-semibold">
Celebrate With Us Rayakan Bersama Kami
</h1> </h1>
<h2 <h2
class="text-blue-700 text-4xl md:text-6xl font-extrabold drop-shadow-md" class="text-blue-700 text-4xl md:text-6xl font-extrabold drop-shadow-md"
@ -22,14 +22,14 @@
{{ childName }} {{ childName }}
</h2> </h2>
<h3 class="text-orange-800 text-2xl md:text-4xl font-bold"> <h3 class="text-orange-800 text-2xl md:text-4xl font-bold">
Birthday Party Pesta Ulang Tahun
</h3> </h3>
<div <div
class="bg-yellow-100/70 border border-yellow-300 shadow-md rounded-xl px-6 py-4 inline-block" class="bg-yellow-100/70 border border-yellow-300 shadow-md rounded-xl px-6 py-4 inline-block"
> >
<p class="text-orange-700 text-lg">Kepada Yth.</p> <p class="text-orange-700 text-lg">Kepada Yth.</p>
<p class="text-orange-800 text-xl font-bold">{{ guestName }}</p> <p class="text-orange-800 text-xl font-bold">{{ guestName || 'Tamu Undangan' }}</p>
</div> </div>
<div> <div>
@ -37,7 +37,7 @@
@click="$emit('open-invitation')" @click="$emit('open-invitation')"
class="mt-6 bg-orange-600 hover:bg-orange-700 text-white px-8 py-3 rounded-full text-lg font-semibold shadow-lg transform hover:scale-105 transition-all" class="mt-6 bg-orange-600 hover:bg-orange-700 text-white px-8 py-3 rounded-full text-lg font-semibold shadow-lg transform hover:scale-105 transition-all"
> >
Open Invitation Buka Undangan
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<template> <template>
<section <section
class="relative w-full min-h-screen bg-gradient-to-b from-blue-900 via-red-800 to-black text-white flex flex-col lg:flex-row items-center justify-between overflow-hidden" class="relative w-full min-h-screen bg-gradient-to-b from-blue-900 via-red-800 to-black text-white flex flex-col lg:flex-row items-center justify-center overflow-hidden"
> >
<!-- 🌐 Efek jaring latar --> <!-- 🌐 Efek jaring latar -->
<div class="absolute inset-0 opacity-10 pointer-events-none"> <div class="absolute inset-0 opacity-10 pointer-events-none">
@ -37,7 +37,7 @@
<!-- ================= KONTEN TEKS (KIRI) ================= --> <!-- ================= KONTEN TEKS (KIRI) ================= -->
<div <div
class="relative z-10 w-full lg:w-1/2 min-h-[50vh] flex flex-col justify-center items-center lg:items-start text-center lg:text-left px-6 lg:px-20 py-12 lg:py-0 space-y-8" class="relative z-10 w-full max-w-lg lg:w-1/2 min-h-[50vh] flex flex-col justify-center items-center text-center px-6 lg:px-12 py-12 space-y-8"
> >
<div class="text-5xl sm:text-6xl animate-bounce">🕷</div> <div class="text-5xl sm:text-6xl animate-bounce">🕷</div>
@ -67,7 +67,7 @@
<!-- Tombol Kembali --> <!-- Tombol Kembali -->
<button <button
@click="reloadPage" @click="reloadPage"
class="w-full bg-gradient-to-r from-red-600 to-blue-600 text-white py-4 px-8 rounded-full font-bold shadow-2xl hover:from-red-700 hover:to-blue-700 transition-all duration-300 transform hover:scale-105 border-2 border-white/30 text-lg flex items-center justify-center gap-3" class="w-full max-w-xs bg-gradient-to-r from-red-600 to-blue-600 text-white py-4 px-8 rounded-full font-bold shadow-2xl hover:from-red-700 hover:to-blue-700 transition-all duration-300 transform hover:scale-105 border-2 border-white/30 text-lg flex items-center justify-center gap-3"
> >
<span class="text-2xl">🕸</span> <span class="text-2xl">🕸</span>
Kembali ke Awal Kembali ke Awal

View File

@ -18,9 +18,9 @@
> >
<li><button @click="switchSection('introduction')" :class="navClass('introduction')" class="px-3 py-1 rounded-full transition-all duration-300">Intro</button></li> <li><button @click="switchSection('introduction')" :class="navClass('introduction')" class="px-3 py-1 rounded-full transition-all duration-300">Intro</button></li>
<li><button @click="switchSection('event')" :class="navClass('event')" class="px-3 py-1 rounded-full transition-all duration-300">Event</button></li> <li><button @click="switchSection('event')" :class="navClass('event')" class="px-3 py-1 rounded-full transition-all duration-300">Event</button></li>
<li><button @click="switchSection('gallery')" :class="navClass('gallery')" class="px-3 py-1 rounded-full transition-all duration-300">Gallery</button></li> <li><button @click="switchSection('gallery')" :class="navClass('gallery')" class="px-3 py-1 rounded-full transition-all duration-300">Galeri</button></li>
<li><button @click="switchSection('guestbook')" :class="navClass('guestbook')" class="px-3 py-1 rounded-full transition-all duration-300">Guest Book</button></li> <li><button @click="switchSection('guestbook')" :class="navClass('guestbook')" class="px-3 py-1 rounded-full transition-all duration-300">Buku Tamu</button></li>
<li><button @click="switchSection('thanks')" :class="navClass('thanks')" class="px-3 py-1 rounded-full transition-all duration-300">Thanks</button></li> <li><button @click="switchSection('thanks')" :class="navClass('thanks')" class="px-3 py-1 rounded-full transition-all duration-300">Terima Kasih</button></li>
</ul> </ul>
</nav> </nav>
@ -40,10 +40,11 @@
> >
<!-- Landing --> <!-- Landing -->
<Landing <Landing
v-if="currentSection === 'landing'" v-if="currentSection === 'landing' && tamu"
:childName="formData.nama_panggilan" :childName="formData.nama_panggilan"
:guestName="data.nama_tamu" :guestName="route.query.guest || tamu.nama_tamu || 'Tamu Undangan'"
@open-invitation="switchSection('introduction')" @open-invitation="switchSection('introduction')"
@toggle-music="toggleMusic"
/> />
<!-- Introduction --> <!-- Introduction -->
@ -72,7 +73,7 @@
<!-- Guest Book --> <!-- Guest Book -->
<GuestBook <GuestBook
v-if="currentSection === 'guestbook'" v-if="currentSection === 'guestbook'"
:guestName="data.nama_tamu" :guestName="route.query.guest || tamu.nama_tamu || 'Tamu Undangan'"
:messages="messages" :messages="messages"
@addMessage="addMessage" @addMessage="addMessage"
/> />
@ -84,14 +85,12 @@
:jsonData="formData" :jsonData="formData"
/> />
</main> </main>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, computed, watchEffect } from 'vue' import { ref, computed, watchEffect } from 'vue'
import { useRuntimeConfig } from '#app' import { useRoute, useRuntimeConfig } from '#app'
// Import Komponen // Import Komponen
import Landing from '~/components/templates/UltahBasic/Landing.vue' import Landing from '~/components/templates/UltahBasic/Landing.vue'
@ -103,15 +102,18 @@ import ThankYou from '~/components/templates/UltahBasic/ThankYou.vue'
// Props dari halaman induk // Props dari halaman induk
const props = defineProps({ const props = defineProps({
data: { type: Object, required: true } data: { type: Object, required: true },
tamu: { type: Object, required: true } // Tambahkan props tamu
}) })
// Runtime config (untuk backend URL) // Runtime config (untuk backend URL)
const config = useRuntimeConfig() const config = useRuntimeConfig()
const backendUrl = config.public.apiBaseUrl const backendUrl = config.public.apiBaseUrl
const route = useRoute()
// Data dari backend // Data dari backend
const formData = computed(() => props.data.form || {}) const formData = computed(() => props.data.form || {})
const messages = computed(() => props.data.rsvp || [])
// Galeri gambar (support foto array atau field lama) // Galeri gambar (support foto array atau field lama)
const galleryImages = computed(() => { const galleryImages = computed(() => {
@ -125,12 +127,16 @@ const galleryImages = computed(() => {
formData.value.foto_3, formData.value.foto_3,
formData.value.foto_4, formData.value.foto_4,
formData.value.foto_5 formData.value.foto_5
].filter(Boolean).map(img => `${backendUrl}/${img}`) ].filter(Boolean).map(img => `${backendUrl}/storage/${img}`)
}) })
// Debug log
watchEffect(() => { watchEffect(() => {
console.log("🧾 formData:", formData.value) console.log("🧾 formData:", formData.value)
console.log("🧾 messages:", messages.value)
console.log("🖼️ galleryImages:", galleryImages.value) console.log("🖼️ galleryImages:", galleryImages.value)
console.log("👤 tamu.nama_tamu:", props.tamu.nama_tamu)
console.log("🚪 route.query.guest:", route.query.guest)
}) })
// Navigasi antar bagian // Navigasi antar bagian
@ -142,7 +148,6 @@ const isPlaying = ref(false)
const toggleMusic = () => (isPlaying.value = !isPlaying.value) const toggleMusic = () => (isPlaying.value = !isPlaying.value)
// Buku tamu // Buku tamu
const messages = ref([])
const addMessage = (msg) => messages.value.push(msg) const addMessage = (msg) => messages.value.push(msg)
// Style untuk navigasi aktif // Style untuk navigasi aktif

View File

@ -1,59 +1,67 @@
<template> <template>
<div class="min-h-screen bg-gradient-to-br from-yellow-300 via-yellow-400 to-yellow-500 relative overflow-hidden"> <div class="min-h-screen bg-gradient-to-br from-yellow-300 via-yellow-400 to-yellow-500 relative overflow-hidden">
<!-- Navigation --> <!-- Navigation -->
<nav v-if="currentSection !== 'landing'" class="absolute top-4 left-1/2 transform -translate-x-1/2 z-20"> <nav v-if="currentSection !== 'landing'" class="absolute top-4 left-1/2 transform -translate-x-1/2 z-20">
<ul <ul class="flex space-x-6 bg-white/40 backdrop-blur-md px-6 py-3 rounded-full shadow-md text-sm font-semibold text-gray-800">
class="flex space-x-6 bg-white/40 backdrop-blur-md px-6 py-3 rounded-full shadow-md text-sm font-semibold text-gray-800"> <li><button @click="switchSection('introduction')" :class="navClass('introduction')">Intro</button></li>
<li><button @click="switchSection('introduction')" :class="navClass('introduction')">Intro</button></li> <li><button @click="switchSection('event')" :class="navClass('event')">Event</button></li>
<li><button @click="switchSection('event')" :class="navClass('event')">Event</button></li> <li><button @click="switchSection('galeri')" :class="navClass('galeri')">Galeri</button></li>
<li><button @click="switchSection('galeri')" :class="navClass('galeri')">Gallery</button></li> <li><button @click="switchSection('say')" :class="navClass('say')">Buku Tamu</button></li>
<li><button @click="switchSection('say')" :class="navClass('say')">Guest Book</button></li> <li><button @click="switchSection('thanks')" :class="navClass('thanks')">Terima Kasih</button></li>
<li><button @click="switchSection('thanks')" :class="navClass('thanks')">Thanks</button></li> </ul>
</ul> </nav>
</nav>
<!-- Tombol Musik --> <!-- Tombol Musik -->
<div class="fixed bottom-4 left-4 z-30" v-if="currentSection !== 'landing'"> <div class="fixed bottom-4 left-4 z-30" v-if="currentSection !== 'landing'">
<button @click="toggleMusic" class="bg-orange-600 p-3 rounded-full text-white shadow-lg"> <button @click="toggleMusic" class="bg-orange-600 p-3 rounded-full text-white shadow-lg">
{{ isPlaying ? '⏸️' : '▶️' }} {{ isPlaying ? '⏸️' : '▶️' }}
</button> </button>
</div>
<main
class="relative z-10 min-h-screen flex items-center justify-center p-4 transition-all duration-700 ease-in-out">
<!-- Landing -->
<Landing v-if="currentSection === 'landing'" :childName="formData.nama_panggilan"
:guestName="data.nama_tamu" @open-invitation="switchSection('introduction')" />
<!-- Introduction -->
<Introduction v-if="currentSection === 'introduction'" :age="formData.umur_yang_dirayakan"
:childName="formData.nama_lengkap" :childOrder="formData.anak_ke"
:parentsName="`${formData.nama_bapak} & ${formData.nama_ibu}`"
:childPhoto="formData.foto && formData.foto.length ? `${backendUrl}/storage/${formData.foto[4]}` : null" />
<!-- Event -->
<Event v-if="currentSection === 'event'" :hari_tanggal_acara="formData.hari_tanggal_acara"
:waktu="formData.waktu" :alamat="formData.alamat" :link_gmaps="formData.link_gmaps"
:hitung_mundur="formData.hitung_mundur" />
<!-- Gallery -->
<Gallery v-if="currentSection === 'galeri'" :images="galleryImages" />
<!-- Guest Book -->
<GuestBook v-if="currentSection === 'say'" :guestName="data.nama_tamu" :messages="messages"
@addMessage="addMessage" />
<!-- Thank You -->
<ThankYou v-if="currentSection === 'thanks'" :childName="formData.nama_panggilan" :jsonData="formData" />
</main>
</div> </div>
<main class="relative z-10 min-h-screen flex items-center justify-center p-4 transition-all duration-700 ease-in-out">
<!-- Landing -->
<Landing
v-if="currentSection === 'landing'"
:childName="formData.nama_panggilan"
:guestName="tamu.nama_tamu || 'Tamu Undangan'"
@open-invitation="switchSection('introduction')"
/>
<!-- Introduction -->
<Introduction
v-if="currentSection === 'introduction'"
:age="formData.umur_yang_dirayakan"
:childName="formData.nama_lengkap"
:childOrder="formData.anak_ke"
:parentsName="`${formData.nama_bapak} & ${formData.nama_ibu}`"
:childPhoto="formData.foto && formData.foto.length ? `${backendUrl}/storage/${formData.foto[4]}` : null"
/>
<!-- Event -->
<Event
v-if="currentSection === 'event'"
:hari_tanggal_acara="formData.hari_tanggal_acara"
:waktu="formData.waktu"
:alamat="formData.alamat"
:link_gmaps="formData.link_gmaps"
:hitung_mundur="formData.hitung_mundur"
/>
<!-- Gallery -->
<Gallery v-if="currentSection === 'galeri'" :images="galleryImages" />
<!-- Guest Book -->
<GuestBook v-if="currentSection === 'say'" :guestName="tamu.nama_tamu || 'Tamu Undangan'" :messages="rsvpData" @addMessage="addMessage" />
<!-- Thank You -->
<ThankYou v-if="currentSection === 'thanks'" :childName="formData.nama_panggilan" :jsonData="formData" />
</main>
</div>
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed, watchEffect } from 'vue'
import { useRuntimeConfig } from '#app' import { useRoute, useRuntimeConfig } from '#app'
// Komponen // Komponen
import Landing from '~/components/templates/Ultah/Landing.vue' import Landing from '~/components/templates/Ultah/Landing.vue'
@ -63,50 +71,44 @@ import Gallery from '~/components/templates/Ultah/Gallery.vue'
import GuestBook from '~/components/templates/Ultah/GuestBook.vue' import GuestBook from '~/components/templates/Ultah/GuestBook.vue'
import ThankYou from '~/components/templates/Ultah/ThankYou.vue' import ThankYou from '~/components/templates/Ultah/ThankYou.vue'
// Props dari halaman /p/[code].vue // Props dari halaman /p/[code].vue
const props = defineProps({ const props = defineProps({
data: { type: Object, required: true } data: { type: Object, required: true },
tamu: { type: Object, required: true } // Tambahkan props tamu
}) })
// Runtime config // Runtime config
const config = useRuntimeConfig() const config = useRuntimeConfig()
const backendUrl = config.public.apiBaseUrl const backendUrl = config.public.apiBaseUrl
const route = useRoute()
// Data form dari backend // Data form dari backend
const formData = computed(() => props.data.form || {}) const formData = computed(() => props.data.form || {})
const rsvpData = computed(() => props.data.rsvp || {})
const galleryImages = computed(() => { const galleryImages = computed(() => {
const f = formData.value.foto const f = formData.value.foto
if (Array.isArray(f)) {
return f.map(img => `${backendUrl}/storage/${img}`)
if (Array.isArray(f)) { }
return f.map(img => `${backendUrl}/storage/${img}`) return [
} formData.value.foto_1,
formData.value.foto_2,
// Jika masih bentuk lama (foto_1, foto_2, dst.) formData.value.foto_3,
return [ formData.value.foto_4,
formData.value.foto_1, formData.value.foto_5
formData.value.foto_2, ].filter(Boolean).map(img => `${backendUrl}/${img}`)
formData.value.foto_3,
formData.value.foto_4,
formData.value.foto_5
].filter(Boolean).map(img => `${backendUrl}/${img}`)
}) })
// Debug log
watchEffect(() => { watchEffect(() => {
console.log("🧾 formData:", formData.value) console.log("🧾 formData:", formData.value)
console.log("🖼️ galleryImages:", galleryImages.value) console.log("🧾 rsvpData:", rsvpData.value)
console.log("🖼️ galleryImages:", galleryImages.value)
console.log("👤 tamu.nama_tamu:", props.tamu.nama_tamu)
console.log("🚪 route.query.guest:", route.query.guest)
}) })
// Navigasi antar section // Navigasi antar section
const currentSection = ref('landing') const currentSection = ref('landing')
const switchSection = (s) => (currentSection.value = s) const switchSection = (s) => (currentSection.value = s)
@ -119,7 +121,7 @@ const toggleMusic = () => (isPlaying.value = !isPlaying.value)
const messages = ref([]) const messages = ref([])
const addMessage = (msg) => messages.value.push(msg) const addMessage = (msg) => messages.value.push(msg)
// Dummy countdown (bisa diganti dinamis nanti) // Dummy countdown
const countdown = ref({ days: 3, hours: 12, minutes: 45, seconds: 20 }) const countdown = ref({ days: 3, hours: 12, minutes: 45, seconds: 20 })
// Style untuk navigasi aktif // Style untuk navigasi aktif

View File

@ -68,6 +68,7 @@
v-bind="componentProps" v-bind="componentProps"
@open-invitation="switchSection('introduction')" @open-invitation="switchSection('introduction')"
@addMessage="addMessage" @addMessage="addMessage"
@toggle-music="toggleMusic"
/> />
</transition> </transition>
</main> </main>
@ -76,7 +77,7 @@
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRuntimeConfig } from '#app' import { useRoute, useRuntimeConfig } from '#app'
// 🧩 Import Semua Komponen // 🧩 Import Semua Komponen
import Landing from '~/components/templates/UltahStarter/Landing.vue' import Landing from '~/components/templates/UltahStarter/Landing.vue'
@ -86,14 +87,19 @@ import Gallery from '~/components/templates/UltahStarter/Gallery.vue'
import ThankYou from '~/components/templates/UltahStarter/ThankYou.vue' import ThankYou from '~/components/templates/UltahStarter/ThankYou.vue'
// Props dari induk // Props dari induk
const props = defineProps({ data: Object }) const props = defineProps({
data: { type: Object, required: true },
tamu: { type: Object, required: true } // Tambahkan props tamu
})
// URL backend // URL backend
const config = useRuntimeConfig() const config = useRuntimeConfig()
const backendUrl = config.public.apiBaseUrl const backendUrl = config.public.apiBaseUrl
const route = useRoute()
// Data backend // Data backend
const formData = computed(() => props.data.form || {}) const formData = computed(() => props.data.form || {})
const messages = computed(() => props.data.rsvp || [])
// Gambar galeri // Gambar galeri
const galleryImages = computed(() => { const galleryImages = computed(() => {
@ -108,8 +114,8 @@ const currentSection = ref('landing')
const sections = [ const sections = [
{ key: 'introduction', label: 'Intro' }, { key: 'introduction', label: 'Intro' },
{ key: 'event', label: 'Event' }, { key: 'event', label: 'Event' },
{ key: 'gallery', label: 'Gallery' }, { key: 'gallery', label: 'Galeri' },
{ key: 'thanks', label: 'Thanks' } { key: 'thanks', label: 'Terima Kasih' }
] ]
const switchSection = (section) => (currentSection.value = section) const switchSection = (section) => (currentSection.value = section)
@ -118,9 +124,7 @@ const switchSection = (section) => (currentSection.value = section)
const isPlaying = ref(false) const isPlaying = ref(false)
const toggleMusic = () => (isPlaying.value = !isPlaying.value) const toggleMusic = () => (isPlaying.value = !isPlaying.value)
// 💬 Buku tamu
const messages = ref([])
const addMessage = (msg) => messages.value.push(msg)
// 🎭 Komponen dinamis berdasarkan section // 🎭 Komponen dinamis berdasarkan section
const currentComponent = computed(() => { const currentComponent = computed(() => {
@ -138,7 +142,7 @@ const currentComponent = computed(() => {
// 🎁 Props untuk tiap komponen // 🎁 Props untuk tiap komponen
const componentProps = computed(() => ({ const componentProps = computed(() => ({
childName: formData.value.nama_panggilan, childName: formData.value.nama_panggilan,
guestName: props.data.nama_tamu, guestName: route.query.guest || props.tamu.nama_tamu || 'Tamu Undangan',
age: formData.value.umur_yang_dirayakan, age: formData.value.umur_yang_dirayakan,
childOrder: formData.value.anak_ke, childOrder: formData.value.anak_ke,
parentsName: `${formData.value.nama_bapak} & ${formData.value.nama_ibu}`, parentsName: `${formData.value.nama_bapak} & ${formData.value.nama_ibu}`,
@ -155,6 +159,16 @@ const componentProps = computed(() => ({
jsonData: formData.value jsonData: formData.value
})) }))
// Debug log
import { watchEffect } from 'vue'
watchEffect(() => {
console.log("🧾 formData:", formData.value)
console.log("🧾 messages:", messages.value)
console.log("🖼️ galleryImages:", galleryImages.value)
console.log("👤 tamu.nama_tamu:", props.tamu.nama_tamu)
console.log("🚪 route.query.guest:", route.query.guest)
})
// Style aktif navigasi // Style aktif navigasi
const navClass = (key) => const navClass = (key) =>
currentSection.value === key currentSection.value === key

View File

@ -135,6 +135,7 @@ const fetchInvitation = async () => {
// Validate response // Validate response
if (!response.success) { if (!response.success) {
throw new Error(response.message || 'Undangan tidak ditemukan') throw new Error(response.message || 'Undangan tidak ditemukan')
console.log('🎫 tamuData:', tamuData.value)
} }
if (!response.data || !response.data.pelanggan) { if (!response.data || !response.data.pelanggan) {