Undangan/proyek-frontend/app/components/templates/wedding/WeddingA.vue
2025-10-24 17:05:32 +07:00

566 lines
29 KiB
Vue

<template>
<div class="wedding-template-a min-h-screen bg-white">
<section
v-if="!isOpened"
class="fixed inset-0 z-50 flex items-center justify-center bg-cover bg-center"
:style="{ backgroundImage: `url(${data.coverImage || '/wedding1.png'})` }"
>
<div class="absolute inset-0 bg-black/50"></div>
<div class="relative text-center text-white p-6 md:p-8 max-w-2xl">
<p class="text-xs uppercase tracking-widest mb-2 opacity-90 font-light"
style="font-family: 'Playfair Display', serif; letter-spacing: 0.2em;">
You are invited to the big day
</p>
<p class="text-sm mb-6 opacity-80" style="font-family: 'Playfair Display', serif;">
We invite you to celebrate our wedding
</p>
<p class="text-lg uppercase tracking-widest mb-4 opacity-90"
style="font-family: 'Playfair Display', serif; letter-spacing: 0.15em;">
THE WEDDING OF
</p>
<h1 class="text-5xl md:text-7xl font-serif mb-8 italic leading-tight"
style="font-family: 'Great Vibes', cursive;">
{{ data.groom?.nickname || 'Asep' }} & {{ data.bride?.nickname || 'Putri' }}
</h1>
<p class="text-xl mb-10 tracking-wide"
style="font-family: 'Playfair Display', serif; letter-spacing: 0.05em;">
{{ formatSimpleDate(data.eventDate) }}
</p>
<p class="text-sm uppercase tracking-widest mb-3 opacity-90"
style="font-family: 'Playfair Display', serif; letter-spacing: 0.15em;">
Kepada Yth.
</p>
<p class="text-base mb-8 font-medium" style="font-family: 'Playfair Display', serif;">
Bapak/Ibu/Saudara/i
</p>
<button @click="openInvitation"
class="px-10 py-3 bg-transparent border-2 border-white text-white rounded-md font-medium text-sm uppercase tracking-wider hover:bg-white hover:text-gray-800 transition-all duration-300"
style="font-family: 'Playfair Display', serif; letter-spacing: 0.1em;">
Open Invitation
</button>
</div>
</section>
<!-- Main Content -->
<div v-show="isOpened" class="animate-fade-in">
<!-- Hero Section -->
<section class="relative h-screen flex items-center justify-center overflow-hidden bg-cover bg-center"
:style="{ backgroundImage: `url(${data.heroImage || '/wedding1.png'})` }">
<div class="absolute inset-0 bg-black/40"></div>
<div class="relative text-center text-white px-4 z-10">
<div class="flex items-center justify-center gap-4 mb-6">
<div class="w-12 h-px bg-white"></div>
<Icon name="mdi:leaf" class="text-2xl" />
<div class="w-12 h-px bg-white"></div>
</div>
<h2 class="text-6xl md:text-8xl font-serif mb-6 italic" style="font-family: 'Great Vibes', cursive;">
{{ data.groom.nickname }} & {{ data.bride.nickname }}
</h2>
<p class="text-lg mb-6" data-aos="fade-up">
#{{ data.groom.nickname }}{{ data.bride.nickname }} #WishesAndWilderness #WeddedintheWoods
</p>
<p class="text-2xl font-light">{{ formatSimpleDate(data.eventDate) }}</p>
</div>
<div class="absolute bottom-8 left-1/2 transform -translate-x-1/2 animate-bounce">
<Icon name="mdi:chevron-down" class="text-white text-3xl" />
</div>
</section>
<!-- Couple Section -->
<section class="py-20 bg-gradient-to-b from-amber-50 via-white to-amber-50">
<div class="max-w-5xl mx-auto px-6">
<div class="text-center mb-16" data-aos="fade-up">
<div class="flex items-center justify-center gap-3 mb-4">
<div class="w-16 h-px bg-amber-400"></div>
<Icon name="mdi:leaf" class="text-amber-500 text-2xl" />
<div class="w-16 h-px bg-amber-400"></div>
</div>
<h2 class="text-4xl md:text-5xl font-bold text-amber-600 mb-4">Meet The Happy Couple</h2>
<p class="text-gray-600 max-w-2xl mx-auto text-sm">
Glory be to God, who has created beings in pairs. With God as guide, we invite you to share this
special day with us as we join together in marriage
</p>
</div>
</div>
<div class="grid md:grid-cols-2 gap-12 max-w-2xl mx-auto">
<!-- Groom -->
<div class="text-center" data-aos="fade-right">
<div class="relative inline-block mb-6">
<div class="w-40 h-40 rounded-full bg-amber-100 mx-auto overflow-hidden border-4 border-white shadow-xl">
<img :src="data.groom.photo || '/pria.jpg'" :alt="data.groom.fullname"
class="w-full h-full object-cover" />
</div>
</div>
<h3 class="text-2xl font-bold text-amber-600 mb-2 italic" style="font-family: 'Great Vibes', cursive;">
{{ data.groom.nickname }}
</h3>
<p class="text-gray-800 font-semibold mb-1">{{ data.groom.fullname }}</p>
<p class="text-sm text-gray-600 mb-3">
Son of Mr. {{ data.groom.fatherName }} & Mrs. {{ data.groom.motherName }}
</p>
<div class="flex justify-center space-x-3">
<a v-if="data.groom.instagram" :href="`https://instagram.com/${data.groom.instagram}`"
target="_blank" class="w-8 h-8 flex items-center justify-center bg-amber-100 rounded-full hover:bg-amber-200 transition-colors">
<Icon name="mdi:instagram" class="text-amber-600 text-lg" />
</a>
<a v-if="data.groom.twitter" :href="`https://twitter.com/${data.groom.twitter}`"
target="_blank" class="w-8 h-8 flex items-center justify-center bg-amber-100 rounded-full hover:bg-amber-200 transition-colors">
<Icon name="mdi:twitter" class="text-amber-600 text-lg" />
</a>
<a v-if="data.groom.facebook" :href="`https://facebook.com/${data.groom.facebook}`"
target="_blank" class="w-8 h-8 flex items-center justify-center bg-amber-100 rounded-full hover:bg-amber-200 transition-colors">
<Icon name="mdi:facebook" class="text-amber-600 text-lg" />
</a>
</div>
</div>
<!-- Bride -->
<div class="text-center" data-aos="fade-left">
<div class="relative inline-block mb-6">
<div class="w-40 h-40 rounded-full bg-amber-100 mx-auto overflow-hidden border-4 border-white shadow-xl">
<img :src="data.bride.photo || '/wanita.jpg'" :alt="data.bride.fullname"
class="w-full h-full object-cover" />
</div>
</div>
<h3 class="text-2xl font-bold text-amber-600 mb-2 italic" style="font-family: 'Great Vibes', cursive;">
{{ data.bride.nickname }}
</h3>
<p class="text-gray-800 font-semibold mb-1">{{ data.bride.fullname }}</p>
<p class="text-sm text-gray-600 mb-3">
Daughter of Mr. {{ data.bride.fatherName }} & Mrs. {{ data.bride.motherName }}
</p>
<div class="flex justify-center space-x-3">
<a v-if="data.bride.instagram" :href="`https://instagram.com/${data.bride.instagram}`"
target="_blank" class="w-8 h-8 flex items-center justify-center bg-amber-100 rounded-full hover:bg-amber-200 transition-colors">
<Icon name="mdi:instagram" class="text-amber-600 text-lg" />
</a>
<a v-if="data.bride.twitter" :href="`https://twitter.com/${data.bride.twitter}`"
target="_blank" class="w-8 h-8 flex items-center justify-center bg-amber-100 rounded-full hover:bg-amber-200 transition-colors">
<Icon name="mdi:twitter" class="text-amber-600 text-lg" />
</a>
<a v-if="data.bride.facebook" :href="`https://facebook.com/${data.bride.facebook}`"
target="_blank" class="w-8 h-8 flex items-center justify-center bg-amber-100 rounded-full hover:bg-amber-200 transition-colors">
<Icon name="mdi:facebook" class="text-amber-600 text-lg" />
</a>
</div>
</div>
</div>
<!-- Our Story Button -->
<div class="text-center mt-12">
<button @click="showStory = !showStory"
class="bg-amber-500 hover:bg-amber-600 text-white px-8 py-3 rounded-md font-medium transition-colors shadow-md">
{{ showStory ? 'Hide Story' : 'Our Story' }}
</button>
</div>
</section>
<!-- Our Story Section (Toggle) -->
<section v-if="showStory" class="py-20 bg-white">
<div class="max-w-4xl mx-auto px-6">
<div class="text-center mb-16" data-aos="fade-up">
<div class="flex items-center justify-center gap-3 mb-4">
<div class="w-16 h-px bg-amber-400"></div>
<Icon name="mdi:leaf" class="text-amber-500 text-2xl" />
<div class="w-16 h-px bg-amber-400"></div>
</div>
<h2 class="text-4xl md:text-5xl font-bold text-amber-600 mb-4">Our Story</h2>
</div>
<div class="grid md:grid-cols-2 gap-8">
<!-- Story with Image -->
<div data-aos="fade-right">
<img :src="data.story?.image1 || '/wedding1.png'" alt="Our Story" class="w-full h-80 object-cover rounded-lg shadow-lg mb-4" />
<p class="text-gray-700 leading-relaxed text-sm">
{{ data.story?.text1 || 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.' }}
</p>
</div>
<div data-aos="fade-left">
<p class="text-gray-700 leading-relaxed text-sm mb-4">
{{ data.story?.text2 || 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.' }}
</p>
<img :src="data.story?.image2 || '/wedding1.png'" alt="Our Story" class="w-full h-80 object-cover rounded-lg shadow-lg" />
</div>
</div>
</div>
</section>
<!-- Event Details -->
<section class="py-20 bg-gradient-to-b from-amber-50 to-amber-50">
<div class="max-w-5xl mx-auto px-6">
<div class="text-center mb-16" data-aos="fade-up">
<div class="flex items-center justify-center gap-3 mb-4">
<div class="w-16 h-px bg-amber-400"></div>
<Icon name="mdi:leaf" class="text-amber-500 text-2xl" />
<div class="w-16 h-px bg-amber-400"></div>
</div>
<h2 class="text-4xl md:text-5xl font-bold text-amber-600 mb-4">We Are Getting Married</h2>
<p class="text-3xl font-serif italic mb-4" style="font-family: 'Great Vibes', cursive;">
#{{ data.groom.nickname }}And{{ data.bride.nickname }} #WishesAndWilderness #WeddedintheWoods
</p>
<p class="text-xl text-gray-700">{{ formatSimpleDate(data.eventDate) }}</p>
</div>
<!-- Countdown -->
<div class="mb-12">
<CountdownTimer :targetDate="data.eventDate" />
</div>
<!-- Event Cards -->
<div class="text-center mb-12">
<div class="flex items-center justify-center gap-3 mb-6">
<div class="w-12 h-px bg-amber-400"></div>
<Icon name="mdi:leaf" class="text-amber-500 text-xl" />
<div class="w-12 h-px bg-amber-400"></div>
</div>
<h3 class="text-3xl font-bold text-amber-600">Our Wedding Celebration</h3>
</div>
<div class="grid md:grid-cols-2 gap-6 max-w-3xl mx-auto">
<!-- Wedding Ceremony -->
<div class="relative bg-cover bg-center h-64 rounded-lg overflow-hidden shadow-xl"
:style="{ backgroundImage: `url(${data.akad.image || '/wedding1.png'})` }" data-aos="fade-right">
<div class="absolute inset-0 bg-gradient-to-b from-black/40 to-black/70"></div>
<div class="relative h-full flex flex-col justify-center items-center text-white p-6">
<Icon name="mdi:ring" class="text-4xl mb-3" />
<h4 class="text-2xl font-bold mb-2">Wedding Ceremony</h4>
<p class="text-sm mb-1">{{ formatSimpleDate(data.akad.date) }}</p>
<p class="text-sm mb-1">{{ data.akad.time }}</p>
<p class="text-sm text-center">{{ data.akad.place }}</p>
</div>
</div>
<!-- Wedding Party -->
<div class="relative bg-cover bg-center h-64 rounded-lg overflow-hidden shadow-xl"
:style="{ backgroundImage: `url(${data.resepsi.image || '/wedding1.png'})` }" data-aos="fade-left">
<div class="absolute inset-0 bg-gradient-to-b from-black/40 to-black/70"></div>
<div class="relative h-full flex flex-col justify-center items-center text-white p-6">
<Icon name="mdi:party-popper" class="text-4xl mb-3" />
<h4 class="text-2xl font-bold mb-2">Wedding Party</h4>
<p class="text-sm mb-1">{{ formatSimpleDate(data.resepsi.date) }}</p>
<p class="text-sm mb-1">{{ data.resepsi.time }}</p>
<p class="text-sm text-center">{{ data.resepsi.place }}</p>
</div>
</div>
</div>
<!-- Maps -->
<div class="mt-12 grid md:grid-cols-2 gap-6 max-w-3xl mx-auto">
<div data-aos="fade-up">
<Maps :location="data.akad.mapUrl" />
</div>
<div data-aos="fade-up">
<Maps :location="data.resepsi.mapUrl" />
</div>
</div>
</div>
</section>
<!-- Gallery -->
<section class="py-20 bg-amber-50">
<div class="max-w-6xl mx-auto px-6">
<div class="text-center mb-16" data-aos="fade-up">
<div class="flex items-center justify-center gap-3 mb-4">
<div class="w-16 h-px bg-amber-400"></div>
<Icon name="mdi:leaf" class="text-amber-500 text-2xl" />
<div class="w-16 h-px bg-amber-400"></div>
</div>
<h2 class="text-4xl md:text-5xl font-bold text-amber-600 mb-4">Gallery</h2>
</div>
<Gallery :images="data.gallery" />
<p class="text-center text-sm text-gray-600 mt-8 max-w-2xl mx-auto">
Thank you to everyone who helped make our special day so wonderful. We are blessed to have you celebrate with us and wish there
could be more smiles, laughs, good food, and company we could share. Truly blessed.
</p>
</div>
</section>
<!-- Gift Section -->
<section class="py-20 bg-gradient-to-b from-amber-50 to-amber-50">
<div class="max-w-4xl mx-auto px-6">
<div class="text-center mb-16" data-aos="fade-up">
<div class="flex items-center justify-center gap-3 mb-4">
<div class="w-16 h-px bg-amber-400"></div>
<Icon name="mdi:leaf" class="text-amber-500 text-2xl" />
<div class="w-16 h-px bg-amber-400"></div>
</div>
<h2 class="text-4xl md:text-5xl font-bold text-amber-600 mb-4">Give a Gift</h2>
<p class="text-gray-600 text-sm">
Your love, laughter, and company on our wedding day is the greatest gift of all. However if you wish
to honor us with a gift, we will gracefully accept it. Thank you!
</p>
</div>
<div class="grid md:grid-cols-2 gap-6">
<!-- Digital Wallet -->
<div class="bg-white rounded-xl shadow-lg p-8 border border-amber-100" data-aos="fade-right">
<h3 class="text-xl font-bold text-amber-600 mb-6">Digital Wallet</h3>
<p class="text-xs text-gray-500 mb-6">Tap card number to copy</p>
<div class="space-y-4">
<div v-for="(account, index) in digitalWallets" :key="index">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">{{ account.name }}</span>
<img :src="account.logo" :alt="account.bank" class="h-5" />
</div>
<div class="flex items-center bg-amber-50 rounded-lg p-3 border border-amber-200 cursor-pointer hover:bg-amber-100 transition-colors"
@click="copyToClipboard(account.number)">
<span class="flex-1 text-gray-700 font-mono text-sm">{{ account.number }}</span>
<Icon name="mdi:content-copy" class="text-amber-600 text-lg" />
</div>
</div>
</div>
</div>
<!-- Offline Gift -->
<div class="bg-white rounded-xl shadow-lg p-8 border border-amber-100" data-aos="fade-left">
<h3 class="text-xl font-bold text-amber-600 mb-6">Offline Gift</h3>
<p class="text-gray-700 text-sm mb-6">
{{ data.gift?.address || 'Jl. Terusan Jakarta No.53, Cicaheum, Kec. Kiaracondong, Kota Bandung, Jawa Barat 40291' }}
</p>
<a :href="data.gift?.mapUrl || 'https://maps.google.com'" target="_blank"
class="flex items-center justify-center gap-2 w-full bg-amber-500 hover:bg-amber-600 text-white px-6 py-3 rounded-lg transition-colors shadow-md">
<Icon name="mdi:map-marker" class="text-xl" />
<span>Open Map</span>
</a>
</div>
</div>
</div>
</section>
<!-- RSVP Section -->
<section class="py-20 bg-gradient-to-b from-amber-50 to-amber-50">
<div class="w-full">
<div class="max-w-2xl mx-auto px-6">
<RSVP :invitationId="data.id" @submitted="handleRSVPSubmit" />
</div>
</div>
</section>
<!-- Footer -->
<footer class="relative py-20 bg-cover bg-center" :style="{ backgroundImage: `url(${data.footerImage || '/wedding1.png'})` }">
<div class="absolute inset-0 bg-gradient-to-b from-black/60 to-black/80"></div>
<div class="relative z-10 text-center text-white px-6">
<div class="mb-8">
<Icon name="mdi:heart" class="text-5xl mb-6" />
</div>
<div class="flex items-center justify-center gap-6 mb-8">
<div class="px-8 py-4 bg-white/20 backdrop-blur-sm rounded-lg border border-white/30">
<p class="text-3xl font-serif italic" style="font-family: 'Great Vibes', cursive;">{{ data.groom.nickname }}</p>
</div>
<span class="text-2xl">-</span>
<div class="px-8 py-4 bg-white/20 backdrop-blur-sm rounded-lg border border-white/30">
<p class="text-3xl font-serif italic" style="font-family: 'Great Vibes', cursive;">{{ data.bride.nickname }}</p>
</div>
</div>
<Icon name="mdi:flower" class="text-3xl mb-6" />
<img src="/ABBAUF.png" alt="Logo" class="h-12 mx-auto mb-4 opacity-90" />
<p class="text-sm opacity-70">© 2024 All rights reserved</p>
</div>
</footer>
<!-- Music Player -->
<MusicPlayer v-if="data.musicUrl" :url="data.musicUrl" :autoplay="true" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import AOS from 'aos'
import 'aos/dist/aos.css'
import CountdownTimer from '~/components/templates/wedding/CountdownTimer.vue'
import Gallery from '~/components/templates/wedding/Gallery.vue'
import Maps from '~/components/templates/wedding/Maps.vue'
import RSVP from '~/components/templates/wedding/RSVP.vue'
import MusicPlayer from '~/components/templates/wedding/MusicPlayer.vue'
const props = defineProps({
data: {
type: Object,
required: true,
default: () => ({
id: '',
coverImage: '/wedding1.png',
heroImage: '/wedding1.png',
footerImage: '/wedding1.png',
greeting: '',
eventDate: new Date(),
bride: {
fullname: '',
nickname: '',
photo: '',
fatherName: '',
motherName: '',
instagram: '',
twitter: '',
facebook: ''
},
groom: {
fullname: '',
nickname: '',
photo: '',
fatherName: '',
motherName: '',
instagram: '',
twitter: '',
facebook: ''
},
akad: {
date: new Date(),
time: '',
place: '',
address: '',
mapUrl: '',
image: ''
},
resepsi: {
date: new Date(),
time: '',
place: '',
address: '',
mapUrl: '',
image: ''
},
story: {
image1: '',
text1: '',
image2: '',
text2: ''
},
gallery: [],
gift: {
address: '',
mapUrl: ''
},
musicUrl: ''
})
}
})
const isOpened = ref(false)
const showStory = ref(false)
const guestMessages = ref([])
const digitalWallets = ref([
{ name: 'Asep Irawan', bank: 'BNI', number: '009 - 0222 2444 21', logo: '/logo1.png' },
{ name: 'Putri Amanda', bank: 'BCA', number: '009 - 0222 2444 21', logo: '/logo2.png' }
])
// Map foto_1 to foto_8 to gallery array
const galleryImages = computed(() => {
const images = [
props.data.foto_1,
props.data.foto_2,
props.data.foto_3,
props.data.foto_4,
props.data.foto_5,
props.data.foto_6,
props.data.foto_7,
props.data.foto_8
].filter(img => img && img !== ''); // Filter out undefined or empty strings
return images.length > 0 ? images : [
'/wedding1.png',
'/pria.jpg',
'/wanita.jpg'
];
})
const openInvitation = () => {
isOpened.value = true
console.log('coverImage:', props.data.coverImage)
console.log('gallery:', galleryImages.value) // Debug gallery images
}
const formatSimpleDate = (date) => {
if (!date) return ''
const d = new Date(date)
const day = d.getDate().toString().padStart(2, '0')
const month = d.getMonth() + 1
const year = d.getFullYear()
return `${day}.${month.toString().padStart(2, '0')}.${year}`
}
const copyToClipboard = async (text) => {
try {
await navigator.clipboard.writeText(text)
alert('Account number copied!')
} catch (err) {
console.error('Failed to copy:', err)
}
}
const handleRSVPSubmit = (data) => {
console.log('RSVP submitted:', data)
if (data.message) {
guestMessages.value.unshift({
name: data.name,
message: data.message,
attendance: data.attendance,
createdAt: new Date()
})
}
}
onMounted(() => {
console.log('props.data:', props.data) // Debug data saat komponen dimuat
AOS.init({
duration: 1000,
once: true,
offset: 100,
easing: 'ease-out-cubic'
})
})
</script>
<style scoped>
@import url('https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap');
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.animate-fade-in {
animation: fade-in 1s ease-out;
}
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: #fef3c7;
}
::-webkit-scrollbar-thumb {
background: #f59e0b;
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: #d97706;
}
</style>
```