Compare commits
	
		
			2 Commits
		
	
	
		
			bdcd09edf8
			...
			510651f7ed
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 510651f7ed | |||
| 3b6063d8c5 | 
| @ -2,3 +2,4 @@ | ||||
| html { | ||||
|   scroll-behavior: smooth; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|     <div class="relative z-10 min-h-screen flex items-center justify-center px-4 md:px-8 py-12"> | ||||
|     <div class="relative z-10 min-h-screen flex items-center justify-center px-4 md:px-8 py-22"> | ||||
|       <div class="text-center max-w-5xl mx-auto"> | ||||
|         <!-- Bismillah --> | ||||
|         <div class="mb-10 animate-fade-in-down"> | ||||
| @ -158,17 +158,21 @@ | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Decorative Element --> | ||||
|     <div class="absolute inset-0 pointer-events-none overflow-hidden"> | ||||
|       <div class="absolute top-0 left-1/2 transform -translate-x-1/2"> | ||||
|         <svg viewBox="0 0 200 100" class="w-40 md:w-48 h-20 md:h-24 text-yellow-400 opacity-20"> | ||||
|           <path | ||||
|             d="M100 20 Q120 0 140 20 Q160 40 140 60 Q120 40 100 60 Q80 40 60 60 Q40 40 60 20 Q80 0 100 20" | ||||
|             fill="currentColor" | ||||
|           /> | ||||
|         </svg> | ||||
|       </div> | ||||
|    <!-- Decorative Lanterns --> | ||||
|     <div class="absolute top-4 left-4 md:top-8 md:left-8 animate-sway"> | ||||
|       <Lantern /> | ||||
|     </div> | ||||
|     <div class="absolute top-4 right-4 md:top-8 md:right-8 animate-sway animation-delay-1000"> | ||||
|       <Lantern /> | ||||
|     </div> | ||||
|      | ||||
|     <!-- Kubah Decorations --> | ||||
|    <div class="absolute top-0 left-8 md:left-32 w-14 md:w-34 kubah-swing animation-delay-300"> | ||||
|   <img src="/kubah.png" alt="Kubah" class="w-full h-auto opacity-80" /> | ||||
| </div> | ||||
| <div class="absolute top-0 right-8 md:right-22 w-14 md:w-44 kubah-swing animation-delay-600"> | ||||
|   <img src="/kubah.png" alt="Kubah" class="w-full h-auto opacity-80" /> | ||||
| </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| @ -294,11 +298,16 @@ onUnmounted(() => { | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| /* Animations & bg-pattern */ | ||||
| /* ========================= | ||||
|    🎨 Background Pattern | ||||
| ========================= */ | ||||
| .bg-pattern { | ||||
|   background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><pattern id='pattern' x='0' y='0' width='20' height='20' patternUnits='userSpaceOnUse'><path d='M10 5 L15 10 L10 15 L5 10 Z' fill='none' stroke='rgba(255,255,255,0.1)' stroke-width='0.5'/></pattern></defs><rect width='100' height='100' fill='url(%23pattern)'/></svg>"); | ||||
| } | ||||
| 
 | ||||
| /* ========================= | ||||
|    ✨ General Animations | ||||
| ========================= */ | ||||
| .animate-sway { | ||||
|   animation: sway 3s ease-in-out infinite; | ||||
| } | ||||
| @ -308,20 +317,37 @@ onUnmounted(() => { | ||||
|   50% { transform: translateY(-10px); } | ||||
| } | ||||
| 
 | ||||
| /* ========================= | ||||
|    🕌 Smooth Kubah Swing | ||||
| ========================= */ | ||||
| .kubah-swing { | ||||
|   animation: kubahSwingSmooth 6s cubic-bezier(0.45, 0, 0.55, 1) infinite; | ||||
|   transform-origin: top center; | ||||
| } | ||||
| 
 | ||||
| @keyframes kubahSwingSmooth { | ||||
|   0% { transform: rotate(0deg); } | ||||
|   20% { transform: rotate(3deg); } | ||||
|   40% { transform: rotate(-3deg); } | ||||
|   60% { transform: rotate(3.5deg); } | ||||
|   80% { transform: rotate(-3deg); } | ||||
|   100% { transform: rotate(0deg); } | ||||
| } | ||||
| 
 | ||||
| /* ========================= | ||||
|    ✨ Fade Animations | ||||
| ========================= */ | ||||
| .animate-fade-in-down { | ||||
|   animation: fadeInDown 1s ease-out forwards; | ||||
|   animation: fadeInDown 0.5s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| .animate-fade-in-up { | ||||
|   animation: fadeInUp 1s ease-out forwards; | ||||
|   animation: fadeInUp 0.5s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| .animate-fade-in-left { | ||||
|   animation: fadeInLeft 1s ease-out forwards; | ||||
|   animation: fadeInLeft 0.5s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| .animate-fade-in-right { | ||||
|   animation: fadeInRight 1s ease-out forwards; | ||||
|   animation: fadeInRight 0.5s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeInDown { | ||||
| @ -344,19 +370,17 @@ onUnmounted(() => { | ||||
|   to { opacity: 1; transform: translateX(0); } | ||||
| } | ||||
| 
 | ||||
| .animation-delay-300 { | ||||
|   animation-delay: 0.3s; | ||||
| } | ||||
| 
 | ||||
| .animation-delay-600 { | ||||
|   animation-delay: 0.6s; | ||||
| } | ||||
| 
 | ||||
| .animation-delay-1000 { | ||||
|   animation-delay: 1s; | ||||
| } | ||||
| /* ========================= | ||||
|    ⏱️ Delay Utilities | ||||
| ========================= */ | ||||
| .animation-delay-300 { animation-delay: 0.3s; } | ||||
| .animation-delay-600 { animation-delay: 0.6s; } | ||||
| .animation-delay-1000 { animation-delay: 0.8s; } | ||||
| 
 | ||||
| /* ========================= | ||||
|    🕌 Font Arabic | ||||
| ========================= */ | ||||
| .arabic-text { | ||||
|   font-family: 'Noto Naskh Arabic', serif; | ||||
| } | ||||
| </style> | ||||
| </style> | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
|     <div class="absolute inset-0 bg-pattern opacity-30"></div> | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|     <div class="relative z-10 flex flex-col items-center justify-center px-6 py-12"> | ||||
|     <div class="relative z-10 flex flex-col items-center justify-center px-6 py-18"> | ||||
|       <!-- Gallery Title --> | ||||
|       <div class="text-center mb-8 animate-fade-in-down"> | ||||
|         <h1 class="text-yellow-400 text-4xl md:text-5xl font-bold mb-6 font-script"> | ||||
| @ -17,40 +17,20 @@ | ||||
|         </p> | ||||
|       </div> | ||||
| 
 | ||||
|       <!-- Photo Gallery Grid --> | ||||
|       <div class="flex-1 max-w-4xl mx-auto w-full animate-fade-in-up animation-delay-300"> | ||||
|         <div class="grid grid-cols-2 md:grid-cols-3 gap-4"> | ||||
|           <!-- Foto pertama (besar) --> | ||||
|       <!-- ✅ Masonry-style Gallery --> | ||||
|       <div class="flex-1 max-w-5xl mx-auto w-full animate-fade-in-up animation-delay-300"> | ||||
|         <div class="columns-2 md:columns-3 gap-4 space-y-4"> | ||||
|           <div | ||||
|             v-if="images.length > 0" | ||||
|             class="md:col-span-1 md:row-span-2 relative group cursor-pointer" | ||||
|             @click="openModal(0)" | ||||
|             v-for="(image, index) in images" | ||||
|             :key="index" | ||||
|             class="relative group break-inside-avoid cursor-pointer" | ||||
|             @click="openModal(index)" | ||||
|           > | ||||
|             <div class="bg-white/10 backdrop-blur-sm rounded-2xl overflow-hidden border border-yellow-400/30 hover:border-yellow-400/60 transition-all duration-300"> | ||||
|               <img | ||||
|                 :src="images[0]" | ||||
|                 :alt="`Gallery Image 1`" | ||||
|                 class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" | ||||
|                 @error="handleImageError" | ||||
|               /> | ||||
|               <div class="absolute inset-0 bg-black/0 group-hover:bg-black/20 flex items-center justify-center transition-colors duration-300"> | ||||
|                 <Icon name="lucide:zoom-in" class="w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-300" /> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|           <!-- Foto lainnya --> | ||||
|           <div | ||||
|             v-for="(image, index) in images.slice(1)" | ||||
|             :key="index + 1" | ||||
|             class="relative group cursor-pointer" | ||||
|             @click="openModal(index + 1)" | ||||
|           > | ||||
|             <div class="bg-white/10 backdrop-blur-sm rounded-xl overflow-hidden border border-yellow-400/30 hover:border-yellow-400/60 transition-all duration-300"> | ||||
|               <img | ||||
|                 :src="image" | ||||
|                 :alt="`Gallery Image ${index + 2}`" | ||||
|                 class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" | ||||
|                 :alt="`Gallery Image ${index + 1}`" | ||||
|                 class="w-full h-auto object-cover group-hover:scale-105 transition-transform duration-300" | ||||
|                 @error="handleImageError" | ||||
|               /> | ||||
|               <div class="absolute inset-0 bg-black/0 group-hover:bg-black/20 flex items-center justify-center transition-colors duration-300"> | ||||
| @ -180,4 +160,9 @@ onUnmounted(() => { | ||||
| .bg-pattern { | ||||
|   background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="islamic" x="0" y="0" width="25" height="25" patternUnits="userSpaceOnUse"><g fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"><path d="M12.5 0 L25 12.5 L12.5 25 L0 12.5 Z M6.25 6.25 L18.75 6.25 L18.75 18.75 L6.25 18.75 Z"/><circle cx="12.5" cy="12.5" r="3"/></g></pattern></defs><rect width="100%" height="100%" fill="url(%23islamic)"/></svg>'); | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| /* Masonry layout fix */ | ||||
| .break-inside-avoid { | ||||
|   break-inside: avoid; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -1,11 +1,12 @@ | ||||
| <!-- components/shared/GuestBook.vue --> | ||||
| <template> | ||||
|   <div class="h-screen w-full relative bg-gradient-to-br from-blue-900 via-blue-800 to-blue-900 overflow-hidden"> | ||||
|   <div class="min-h-screen w-full relative bg-gradient-to-br from-blue-900 via-blue-800 to-blue-900 overflow-x-hidden overflow-y-auto"> | ||||
| 
 | ||||
|     <!-- Background Pattern --> | ||||
|     <div class="absolute inset-0 bg-pattern opacity-30"></div> | ||||
|      | ||||
|     <!-- Main Content --> | ||||
|     <div class="relative z-10 h-full flex items-center justify-center px-6 py-12"> | ||||
|     <div class="relative z-10 h-full flex items-center justify-center px-6 py-18"> | ||||
|       <div class="w-full max-w-6xl mx-auto"> | ||||
|          | ||||
|         <!-- Title --> | ||||
|  | ||||
| @ -2,7 +2,10 @@ | ||||
|   <div | ||||
|     class="h-screen w-full relative bg-gradient-to-br from-blue-900 via-blue-800 to-blue-900 overflow-hidden" | ||||
|   > | ||||
|    <!-- Main Content --> | ||||
|     <!-- Background Pattern --> | ||||
|     <div class="absolute inset-0 bg-pattern opacity-30"></div> | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|     <div class="relative z-10 min-h-screen flex items-center justify-center px-4 md:px-8 py-12"> | ||||
|       <div class="text-center max-w-4xl mx-auto"> | ||||
|         <!-- Title --> | ||||
| @ -11,49 +14,44 @@ | ||||
|             Introduction | ||||
|           </h1> | ||||
|         </div> | ||||
|          | ||||
| 
 | ||||
|     <!-- Background Pattern --> | ||||
|     <div class="absolute inset-0 bg-pattern opacity-30"></div> | ||||
|         <!-- Child Section --> | ||||
|         <div class="relative z-10 h-full flex flex-col items-center justify-center px-6 text-center"> | ||||
|           <!-- Child Photo --> | ||||
|           <div class="w-68 h-80 overflow-hidden rounded-t-3xl mx-auto mb-[-20px] shadow-lg"> | ||||
|             <img | ||||
|               :src="getFullPhotoUrl(childPhoto)" | ||||
|               alt="Child" | ||||
|               class="w-full h-full object-cover" | ||||
|             /> | ||||
|           </div> | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|     <div | ||||
|       class="relative z-10 h-full flex flex-col items-center justify-center px-6 text-center" | ||||
|     > | ||||
|       <!-- Child Photo --> | ||||
|       <div class="w-48 h-60 overflow-hidden rounded-t-3xl mx-auto mb-6 shadow-lg"> | ||||
|         <img | ||||
|           :src="getFullPhotoUrl(childPhoto)" | ||||
|           alt="Child" | ||||
|           class="w-full h-full object-cover" | ||||
|         /> | ||||
|       </div> | ||||
| <!-- Child Name with nickname.png background (Tailwind version) --> | ||||
| <div | ||||
|   class="relative mb-0 inline-flex items-center justify-center w-[420px] h-[110px] bg-[url('/nickname.png')] bg-contain bg-no-repeat bg-center" | ||||
| > | ||||
|   <h1 class="text-yellow-400 font-bold text-xl md:text-2xl font-script drop-shadow-lg"> | ||||
|     {{ form.nama_lengkap || 'Nama Anak' }} | ||||
|   </h1> | ||||
| </div> | ||||
| 
 | ||||
|       <!-- Child Name --> | ||||
|       <div | ||||
|         class="bg-blue-800/50 border border-yellow-400 rounded-md px-6 py-2 mb-4 inline-block" | ||||
|       > | ||||
|         <h1 class="text-yellow-400 font-bold text-xl"> | ||||
|           {{ form.nama_lengkap || 'Nama Anak' }} | ||||
|         </h1> | ||||
|       </div> | ||||
| 
 | ||||
|       <!-- Child Order --> | ||||
|       <div v-if="form.anak_ke" class="text-white/80 text-lg mb-2"> | ||||
|         Putra ke {{ form.anak_ke }} dari | ||||
|       </div> | ||||
|       <div v-else class="text-white/80 text-lg mb-2"> | ||||
|         Putra dari | ||||
|       </div> | ||||
|           <!-- Child Order --> | ||||
|           <div v-if="form.anak_ke" class="text-white/80 text-lg mb-2"> | ||||
|             Putra ke {{ form.anak_ke }} dari | ||||
|           </div> | ||||
|           <div v-else class="text-white/80 text-lg mb-2"> | ||||
|             Putra dari | ||||
|           </div> | ||||
| 
 | ||||
|       <!-- Parents --> | ||||
|       <div class="text-yellow-400 font-semibold text-xl"> | ||||
|         {{ form.nama_bapak || 'Nama Ayah' }} & {{ form.nama_ibu || 'Nama Ibu' }} | ||||
|           <!-- Parents --> | ||||
|           <div class="text-yellow-400 font-semibold text-xl"> | ||||
|             {{ form.nama_bapak || 'Nama Ayah' }} & {{ form.nama_ibu || 'Nama Ibu' }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   </div> | ||||
|         </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| @ -89,4 +87,20 @@ const getFullPhotoUrl = (photo) => { | ||||
| .bg-pattern { | ||||
|   background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="islamic" x="0" y="0" width="25" height="25" patternUnits="userSpaceOnUse"><g fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"><path d="M12.5 0 L25 12.5 L12.5 25 L0 12.5 Z M6.25 6.25 L18.75 6.25 L18.75 18.75 L6.25 18.75 Z"/><circle cx="12.5" cy="12.5" r="3"/></g></pattern></defs><rect width="100%" height="100%" fill="url(%23islamic)"/></svg>'); | ||||
| } | ||||
| 
 | ||||
| /* Efek animasi halus muncul dari atas */ | ||||
| @keyframes fade-in-down { | ||||
|   from { | ||||
|     opacity: 0; | ||||
|     transform: translateY(-20px); | ||||
|   } | ||||
|   to { | ||||
|     opacity: 1; | ||||
|     transform: translateY(0); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .animate-fade-in-down { | ||||
|   animation: fade-in-down 1s ease-out; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -1,33 +1,55 @@ | ||||
| ```vue | ||||
| <template> | ||||
|   <div class="min-h-screen w-full relative bg-gradient-to-br from-blue-900 via-blue-800 to-blue-900 overflow-hidden"> | ||||
|     <!-- Background Pattern --> | ||||
|   <div | ||||
|     class="min-h-screen w-full relative bg-gradient-to-br from-blue-900 via-blue-800 to-blue-900 overflow-hidden" | ||||
|   > | ||||
|     <!-- 🌙 Background Pattern --> | ||||
|     <div class="absolute inset-0 bg-pattern opacity-20"></div> | ||||
| 
 | ||||
|     <!-- Main Content --> | ||||
|   <!-- Kubah Decorations --> | ||||
|    <div class="absolute top-0 left-8 md:left-32 w-16 md:w-34 kubah-swing animation-delay-300"> | ||||
|   <img src="/kubah.png" alt="Kubah" class="w-full h-auto opacity-80" /> | ||||
| </div> | ||||
| <div class="absolute top-0 right-8 md:right-22 w-16 md:w-44 kubah-swing animation-delay-600"> | ||||
|   <img src="/kubah.png" alt="Kubah" class="w-full h-auto opacity-80" /> | ||||
| </div> | ||||
| 
 | ||||
|     <!-- 🌸 Main Content --> | ||||
|     <div class="relative z-10 min-h-screen flex items-center justify-center px-4 md:px-8 py-12"> | ||||
|       <div class="text-center max-w-4xl mx-auto"> | ||||
|         <!-- Title --> | ||||
|         <div class="mb-10 animate-fade-in-down"> | ||||
|           <h1 class="text-yellow-400 text-3xl md:text-5xl lg:text-6xl font-bold mb-4 md:mb-6 font-script"> | ||||
|           <h1 | ||||
|             class="text-yellow-400 text-3xl md:text-5xl lg:text-6xl font-bold mb-4 md:mb-6 font-script" | ||||
|           > | ||||
|             Terima Kasih | ||||
|           </h1> | ||||
|           <p class="text-white text-sm md:text-base lg:text-lg leading-relaxed max-w-3xl mx-auto"> | ||||
|           <p | ||||
|             class="text-white text-sm md:text-base lg:text-lg leading-relaxed max-w-3xl mx-auto" | ||||
|           > | ||||
|             Kami mengucapkan terima kasih atas kehadiran serta doa restu yang diberikan untuk | ||||
|             <span class="text-yellow-400 font-semibold">{{ jsonData.nama_panggilan || 'putra kami' }}</span>. | ||||
|             <span class="text-yellow-400 font-semibold">{{ | ||||
|               jsonData.nama_panggilan || "putra kami" | ||||
|             }}</span>. | ||||
|             Semoga Allah SWT senantiasa melimpahkan rahmat dan keberkahan kepada kita semua. | ||||
|           </p> | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- Family Info --> | ||||
|         <div class="mb-12 animate-fade-in-up animation-delay-300"> | ||||
|           <h2 class="text-yellow-400 text-xl md:text-2xl lg:text-3xl font-bold mb-6 md:mb-8"> | ||||
|           <h2 | ||||
|             class="text-yellow-400 text-xl md:text-2xl lg:text-3xl font-bold mb-6 md:mb-8" | ||||
|           > | ||||
|             Kami Keluarga Besar Dari | ||||
|           </h2> | ||||
|           <div class="space-y-4 md:space-y-6"> | ||||
|             <div class="bg-white/10 backdrop-blur-md rounded-2xl p-4 md:p-6 border border-yellow-400/30 shadow-lg animate-fade-in-up animation-delay-500"> | ||||
|               <div class="text-yellow-400 text-base md:text-lg font-semibold mb-2"> | ||||
|                 {{ jsonData.nama_bapak || 'Bpk H. Munawar Huda, S.H.' }} & {{ jsonData.nama_ibu || 'Ibu Hj. Dinah, A.M.Keb' }} | ||||
|             <div | ||||
|               class="bg-white/10 backdrop-blur-md rounded-2xl p-4 md:p-6 border border-yellow-400/30 shadow-lg animate-fade-in-up animation-delay-500" | ||||
|             > | ||||
|               <div | ||||
|                 class="text-yellow-400 text-base md:text-lg font-semibold mb-2" | ||||
|               > | ||||
|                 {{ jsonData.nama_bapak || "Bpk H. Munawar Huda, S.H." }} & | ||||
|                 {{ jsonData.nama_ibu || "Ibu Hj. Dinah, A.M.Keb" }} | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
| @ -35,91 +57,147 @@ | ||||
| 
 | ||||
|         <!-- Final Message --> | ||||
|         <div class="animate-fade-in-up animation-delay-1200"> | ||||
|           <div class="bg-gradient-to-r from-yellow-400/20 to-yellow-600/20 backdrop-blur-md rounded-2xl p-6 md:p-8 border border-yellow-400/50 shadow-lg"> | ||||
|           <div | ||||
|             class="bg-gradient-to-r from-yellow-400/20 to-yellow-600/20 backdrop-blur-md rounded-2xl p-6 md:p-8 border border-yellow-400/50 shadow-lg" | ||||
|           > | ||||
|             <p class="text-white text-base md:text-lg font-medium mb-4 italic"> | ||||
|               "Dan Allah telah mengeluarkan kamu dari perut ibumu dalam keadaan tidak mengetahui sesuatupun..." | ||||
|               "Dan Allah telah mengeluarkan kamu dari perut ibumu dalam keadaan | ||||
|               tidak mengetahui sesuatupun..." | ||||
|             </p> | ||||
|             <p class="text-yellow-400 text-sm md:text-base font-semibold"> | ||||
|               - QS. An-Nahl: 78 | ||||
|             </p> | ||||
|             <p class="text-yellow-400 text-sm md:text-base font-semibold">- QS. An-Nahl: 78</p> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- Social Media Links --> | ||||
|         <div class="mt-10 md:mt-12 animate-fade-in-up animation-delay-1500 flex justify-center space-x-4 md:space-x-6"> | ||||
|           <a | ||||
|             v-if="jsonData.link_instagram" | ||||
|             :href="jsonData.link_instagram" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             class="rounded-full p-2 md:p-3 backdrop-blur-md border border-yellow-400/30 hover:border-yellow-400/60 hover:bg-white/20 transition-all duration-300 transform hover:scale-110" | ||||
|             aria-label="Instagram" | ||||
|           > | ||||
|             <Icon name="lucide:instagram" class="w-5 h-5 md:w-6 md:h-6 text-yellow-400" /> | ||||
|           </a> | ||||
|           <a | ||||
|             v-if="jsonData.link_facebook" | ||||
|             :href="jsonData.link_facebook" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             class="rounded-full p-2 md:p-3 backdrop-blur-md border border-yellow-400/30 hover:border-yellow-400/60 hover:bg-white/20 transition-all duration-300 transform hover:scale-110" | ||||
|             aria-label="Facebook" | ||||
|           > | ||||
|             <Icon name="lucide:facebook" class="w-5 h-5 md:w-6 md:h-6 text-yellow-400" /> | ||||
|           </a> | ||||
|           <a | ||||
|             v-if="jsonData.link_twitter" | ||||
|             :href="jsonData.link_twitter" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             class="rounded-full p-2 md:p-3 backdrop-blur-md border border-yellow-400/30 hover:border-yellow-400/60 hover:bg-white/20 transition-all duration-300 transform hover:scale-110" | ||||
|             aria-label="Twitter" | ||||
|           > | ||||
|             <Icon name="lucide:twitter" class="w-5 h-5 md:w-6 md:h-6 text-yellow-400" /> | ||||
|           </a> | ||||
|         </div> | ||||
|  <!-- Social Media Links --> | ||||
| <div | ||||
|   class="mt-10 md:mt-12 animate-fade-in-up animation-delay-1500 flex justify-center space-x-4 md:space-x-6" | ||||
| > | ||||
|   <a | ||||
|     v-if="jsonData.link_instagram" | ||||
|     :href="jsonData.link_instagram" | ||||
|     target="_blank" | ||||
|     rel="noopener noreferrer" | ||||
|     class="w-10 h-10 md:w-12 md:h-12 flex items-center justify-center rounded-full backdrop-blur-md border border-yellow-400/30 hover:border-yellow-400/60 hover:bg-white/20 transition-all duration-300 transform hover:scale-110" | ||||
|     aria-label="Instagram" | ||||
|   > | ||||
|     <Icon | ||||
|       name="lucide:instagram" | ||||
|       class="w-5 h-5 md:w-6 md:h-6 text-yellow-400" | ||||
|     /> | ||||
|   </a> | ||||
| 
 | ||||
|   <a | ||||
|     v-if="jsonData.link_facebook" | ||||
|     :href="jsonData.link_facebook" | ||||
|     target="_blank" | ||||
|     rel="noopener noreferrer" | ||||
|     class="w-10 h-10 md:w-12 md:h-12 flex items-center justify-center rounded-full backdrop-blur-md border border-yellow-400/30 hover:border-yellow-400/60 hover:bg-white/20 transition-all duration-300 transform hover:scale-110" | ||||
|     aria-label="Facebook" | ||||
|   > | ||||
|     <Icon | ||||
|       name="lucide:facebook" | ||||
|       class="w-5 h-5 md:w-6 md:h-6 text-yellow-400" | ||||
|     /> | ||||
|   </a> | ||||
| 
 | ||||
|   <a | ||||
|     v-if="jsonData.link_twitter" | ||||
|     :href="jsonData.link_twitter" | ||||
|     target="_blank" | ||||
|     rel="noopener noreferrer" | ||||
|     class="w-10 h-10 md:w-12 md:h-12 flex items-center justify-center rounded-full backdrop-blur-md border border-yellow-400/30 hover:border-yellow-400/60 hover:bg-white/20 transition-all duration-300 transform hover:scale-110" | ||||
|     aria-label="Twitter" | ||||
|   > | ||||
|     <Icon | ||||
|       name="lucide:twitter" | ||||
|       class="w-5 h-5 md:w-6 md:h-6 text-yellow-400" | ||||
|     /> | ||||
|   </a> | ||||
| </div> | ||||
| 
 | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Copyright --> | ||||
|     <!-- Footer --> | ||||
|     <div class="absolute bottom-4 left-1/2 transform -translate-x-1/2 z-10"> | ||||
|       <p class="text-white/60 text-xs text-center">© {{ currentYear }} - Invitation Template</p> | ||||
|       <p class="text-white/60 text-xs text-center"> | ||||
|         © {{ currentYear }} - Invitation Template | ||||
|       </p> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { computed } from 'vue' | ||||
| import { computed } from "vue"; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   childName: { | ||||
|     type: String, | ||||
|     default: '' | ||||
|     default: "", | ||||
|   }, | ||||
|   jsonData: { | ||||
|     type: Object, | ||||
|     default: () => ({}) | ||||
|   } | ||||
| }) | ||||
|     default: () => ({}), | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const currentYear = computed(() => new Date().getFullYear()) | ||||
| const currentYear = computed(() => new Date().getFullYear()); | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| /* Animations & bg-pattern */ | ||||
| /* ========================= | ||||
|    🎨 Background Pattern | ||||
| ========================= */ | ||||
| .bg-pattern { | ||||
|   background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><pattern id='pattern' x='0' y='0' width='20' height='20' patternUnits='userSpaceOnUse'><path d='M10 5 L15 10 L10 15 L5 10 Z' fill='none' stroke='rgba(255,255,255,0.1)' stroke-width='0.5'/></pattern></defs><rect width='100' height='100' fill='url(%23pattern)'/></svg>"); | ||||
| } | ||||
| 
 | ||||
| .animate-fade-in-up { | ||||
|   animation: fadeInUp 1s ease-out forwards; | ||||
| /* ========================= | ||||
|    ✨ General Animations | ||||
| ========================= */ | ||||
| .animate-sway { | ||||
|   animation: sway 3s ease-in-out infinite; | ||||
| } | ||||
| 
 | ||||
| @keyframes sway { | ||||
|   0%, 100% { transform: translateY(0); } | ||||
|   50% { transform: translateY(-10px); } | ||||
| } | ||||
| 
 | ||||
| /* ========================= | ||||
|    🕌 Smooth Kubah Swing | ||||
| ========================= */ | ||||
| .kubah-swing { | ||||
|   animation: kubahSwingSmooth 6s cubic-bezier(0.45, 0, 0.55, 1) infinite; | ||||
|   transform-origin: top center; | ||||
| } | ||||
| 
 | ||||
| @keyframes kubahSwingSmooth { | ||||
|   0% { transform: rotate(0deg); } | ||||
|   20% { transform: rotate(2.5deg); } | ||||
|   40% { transform: rotate(-2.5deg); } | ||||
|   60% { transform: rotate(2deg); } | ||||
|   80% { transform: rotate(-2deg); } | ||||
|   100% { transform: rotate(0deg); } | ||||
| } | ||||
| 
 | ||||
| /* ========================= | ||||
|    ✨ Fade Animations | ||||
| ========================= */ | ||||
| .animate-fade-in-down { | ||||
|   animation: fadeInDown 1s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeInUp { | ||||
|   from { opacity: 0; transform: translateY(20px); } | ||||
|   to { opacity: 1; transform: translateY(0); } | ||||
| .animate-fade-in-up { | ||||
|   animation: fadeInUp 1s ease-out forwards; | ||||
| } | ||||
| .animate-fade-in-left { | ||||
|   animation: fadeInLeft 1s ease-out forwards; | ||||
| } | ||||
| .animate-fade-in-right { | ||||
|   animation: fadeInRight 1s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| @keyframes fadeInDown { | ||||
| @ -127,20 +205,32 @@ const currentYear = computed(() => new Date().getFullYear()) | ||||
|   to { opacity: 1; transform: translateY(0); } | ||||
| } | ||||
| 
 | ||||
| .animation-delay-300 { | ||||
|   animation-delay: 0.3s; | ||||
| @keyframes fadeInUp { | ||||
|   from { opacity: 0; transform: translateY(20px); } | ||||
|   to { opacity: 1; transform: translateY(0); } | ||||
| } | ||||
| 
 | ||||
| .animation-delay-500 { | ||||
|   animation-delay: 0.5s; | ||||
| @keyframes fadeInLeft { | ||||
|   from { opacity: 0; transform: translateX(-20px); } | ||||
|   to { opacity: 1; transform: translateX(0); } | ||||
| } | ||||
| 
 | ||||
| .animation-delay-1200 { | ||||
|   animation-delay: 1.2s; | ||||
| @keyframes fadeInRight { | ||||
|   from { opacity: 0; transform: translateX(20px); } | ||||
|   to { opacity: 1; transform: translateX(0); } | ||||
| } | ||||
| 
 | ||||
| .animation-delay-1500 { | ||||
|   animation-delay: 1.5s; | ||||
| /* ========================= | ||||
|    ⏱️ Delay Utilities | ||||
| ========================= */ | ||||
| .animation-delay-300 { animation-delay: 0.3s; } | ||||
| .animation-delay-600 { animation-delay: 0.6s; } | ||||
| .animation-delay-1000 { animation-delay: 1s; } | ||||
| 
 | ||||
| /* ========================= | ||||
|    🕌 Font Arabic | ||||
| ========================= */ | ||||
| .arabic-text { | ||||
|   font-family: 'Noto Naskh Arabic', serif; | ||||
| } | ||||
| </style> | ||||
| ``` | ||||
| @ -31,7 +31,6 @@ | ||||
|     <main | ||||
|       class="relative z-10 min-h-screen flex items-center justify-center p-4 transition-all duration-700 ease-in-out" | ||||
|     > | ||||
|       <!-- Landing Page --> | ||||
|       <KhitanA | ||||
|         v-if="currentSection === 'landing'" | ||||
|         :childName="formData.nama_panggilan" | ||||
| @ -39,37 +38,30 @@ | ||||
|         @next-page="switchSection('introduction')" | ||||
|       /> | ||||
| 
 | ||||
|       <!-- Introduction --> | ||||
|          <KhitanIntroduction | ||||
|       <KhitanIntroduction | ||||
|         v-if="currentSection === 'introduction'" | ||||
|        :form="formData" | ||||
|         /> | ||||
|         :form="formData" | ||||
|       /> | ||||
| 
 | ||||
|       <KhitanEvent | ||||
|         v-if="currentSection === 'event'" | ||||
|         :hari_tanggal_acara="formData.hari_tanggal_acara" | ||||
|         :waktu="formData.waktu" | ||||
|         :alamat="formData.alamat" | ||||
|         :link_gmaps="formData.link_gmaps" | ||||
|         :hitung_mundur_mulai="formData.hitung_mundur_mulai" | ||||
|         :hari_tanggal_syukuran="formData.hari_tanggal_syukuran" | ||||
|         :waktu_syukuran="formData.waktu_syukuran" | ||||
|         :alamat_syukuran="formData.alamat_syukuran" | ||||
|         :link_gmaps_syukuran="formData.link_gmaps_syukuran" | ||||
|         :hitung_mundur_selesai="formData.hitung_mundur_selesai" | ||||
|       /> | ||||
| 
 | ||||
|       <!-- Event --> | ||||
| <KhitanEvent | ||||
|   v-if="currentSection === 'event'" | ||||
|   :hari_tanggal_acara="formData.hari_tanggal_acara" | ||||
|   :waktu="formData.waktu" | ||||
|   :alamat="formData.alamat" | ||||
|   :link_gmaps="formData.link_gmaps" | ||||
|   :hitung_mundur_mulai="formData.hitung_mundur_mulai" | ||||
| 
 | ||||
|   :hari_tanggal_syukuran="formData.hari_tanggal_syukuran" | ||||
|   :waktu_syukuran="formData.waktu_syukuran" | ||||
|   :alamat_syukuran="formData.alamat_syukuran" | ||||
|   :link_gmaps_syukuran="formData.link_gmaps_syukuran" | ||||
|   :hitung_mundur_selesai="formData.hitung_mundur_selesai" | ||||
| /> | ||||
| 
 | ||||
| 
 | ||||
|       <!-- Gallery --> | ||||
|       <KhitanGallery | ||||
|         v-if="currentSection === 'gallery'" | ||||
|         :images="galleryImages" | ||||
|       /> | ||||
| 
 | ||||
|       <!-- Guest Book --> | ||||
|       <KhitanSay | ||||
|         v-if="currentSection === 'say'" | ||||
|         :guestName="data.nama_tamu" | ||||
| @ -77,18 +69,18 @@ | ||||
|         @addMessage="addMessage" | ||||
|       /> | ||||
| 
 | ||||
|        | ||||
|       <!-- Thank You --> | ||||
|             <KhitanThankYou v-if="currentSection === 'thanks'" :childName="formData.nama_panggilan" :jsonData="formData" /> | ||||
|       <KhitanThankYou | ||||
|         v-if="currentSection === 'thanks'" | ||||
|         :childName="formData.nama_panggilan" | ||||
|         :jsonData="formData" | ||||
|       /> | ||||
|     </main> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { ref, computed, watch } from 'vue' | ||||
| import { ref, computed } from 'vue' | ||||
| import { useRuntimeConfig } from '#app' | ||||
| 
 | ||||
| // ================== IMPORT KOMPONEN ================== | ||||
| import KhitanA from '~/components/templates/khitan/KhitanA.vue' | ||||
| import KhitanIntroduction from '~/components/templates/khitan/Introduction.vue' | ||||
| import KhitanEvent from '~/components/templates/khitan/Event.vue' | ||||
| @ -96,48 +88,30 @@ import KhitanGallery from '~/components/templates/khitan/Gallery.vue' | ||||
| import KhitanSay from '~/components/templates/khitan/GuestBook.vue' | ||||
| import KhitanThankYou from '~/components/templates/khitan/ThankYou.vue' | ||||
| 
 | ||||
| // ================== PROPS ================== | ||||
| const props = defineProps({ | ||||
|   data: { type: Object, required: true } | ||||
| }) | ||||
| 
 | ||||
| // ================== BACKEND CONFIG ================== | ||||
| const config = useRuntimeConfig() | ||||
| const backendUrl = config.public.apiBaseUrl | ||||
| 
 | ||||
| // ================== FORM DATA ================== | ||||
| const formData = computed(() => props.data.form || {}) | ||||
| 
 | ||||
| // ================== GALERI ================== | ||||
| const galleryImages = computed(() => { | ||||
|     const f = formData.value.foto | ||||
| 
 | ||||
| 
 | ||||
|     if (Array.isArray(f)) { | ||||
|         return f.map(img => `${backendUrl}/storage/${img}`) | ||||
|     } | ||||
| 
 | ||||
|     // Jika masih bentuk lama (foto_1, foto_2, dst.) | ||||
|     return [ | ||||
|         formData.value.foto_1, | ||||
|         formData.value.foto_2, | ||||
|         formData.value.foto_3, | ||||
|         formData.value.foto_4, | ||||
|         formData.value.foto_5 | ||||
|     ].filter(Boolean).map(img => `${backendUrl}/${img}`) | ||||
|   const f = formData.value.foto | ||||
|   if (Array.isArray(f)) { | ||||
|     return f.map(img => `${backendUrl}/storage/${img}`) | ||||
|   } | ||||
|   return [ | ||||
|     formData.value.foto_1, | ||||
|     formData.value.foto_2, | ||||
|     formData.value.foto_3, | ||||
|     formData.value.foto_4, | ||||
|     formData.value.foto_5 | ||||
|   ].filter(Boolean).map(img => `${backendUrl}/${img}`) | ||||
| }) | ||||
| 
 | ||||
| // Foto utama diambil dari foto pertama | ||||
| const mainPhoto = computed(() => { | ||||
|   const firstPhoto = formData.value.foto?.[0] | ||||
|   return firstPhoto ? `${backendUrl}/${firstPhoto}` : '' | ||||
| }) | ||||
| 
 | ||||
| // ================== NAVIGASI SECTION ================== | ||||
| const currentSection = ref('landing') | ||||
| const switchSection = (s) => (currentSection.value = s) | ||||
| 
 | ||||
| // ================== MUSIK ================== | ||||
| const audioPlayer = ref(null) | ||||
| const isPlaying = ref(false) | ||||
| const musicUrl = computed(() => | ||||
| @ -154,11 +128,9 @@ const toggleMusic = () => { | ||||
|   isPlaying.value = !isPlaying.value | ||||
| } | ||||
| 
 | ||||
| // ================== GUEST BOOK ================== | ||||
| const messages = ref([]) | ||||
| const addMessage = (msg) => messages.value.push(msg) | ||||
| 
 | ||||
| // ================== STYLE NAV ================== | ||||
| const navClass = (s) => | ||||
|   currentSection.value === s | ||||
|     ? 'text-blue-800 underline' | ||||
| @ -166,6 +138,31 @@ const navClass = (s) => | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| /* Pastikan benar-benar fullscreen */ | ||||
| html, | ||||
| body, | ||||
| :host, | ||||
| div[min-h-screen], | ||||
| .min-h-screen { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
| } | ||||
| 
 | ||||
| /* Hilangkan white space akibat scroll */ | ||||
| body { | ||||
|   overflow-x: hidden; | ||||
|   background: transparent; | ||||
| } | ||||
| 
 | ||||
| /* Pastikan gradient menutup seluruh layar */ | ||||
| div[min-h-screen] { | ||||
|   position: relative; | ||||
|   inset: 0; | ||||
| } | ||||
| 
 | ||||
| /* Transisi section tetap */ | ||||
| main { | ||||
|   transition: all 0.7s ease-in-out; | ||||
| } | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								proyek-frontend/public/kubah.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								proyek-frontend/public/kubah.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 51 KiB | 
							
								
								
									
										
											BIN
										
									
								
								proyek-frontend/public/nickname.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								proyek-frontend/public/nickname.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 77 KiB | 
		Loading…
	
		Reference in New Issue
	
	Block a user