import React, { useState, useEffect, useRef } from 'react';
import {
Heart, Calendar, MapPin, Star, Menu, X, Instagram, Mail, Phone,
ChevronRight, Sparkles, Camera, Lock, Save, LogOut, Loader2,
CheckCircle, ArrowLeft, ChevronLeft, MessageCircle, Plus, Minus, Feather, Quote
} from 'lucide-react';
// ⚠️ PENTING: MASUKKAN URL GOOGLE APPS SCRIPT ANDA DI SINI
const GOOGLE_SCRIPT_URL = "https://script.google.com/macros/s/AKfycbwsSPmOLtgdhYG61bDDY_xKNLKFKTgRz0SId9_g23N-VC7WS-d1UBN4xYOH_Nur5Z_S/exec";
// ==========================================
// KOMPONEN DASHBOARD ADMIN
// ==========================================
const AdminPanel = ({ initialData, onBack }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [password, setPassword] = useState('');
const [formData, setFormData] = useState({});
const [isSaving, setIsSaving] = useState(false);
const [toast, setToast] = useState({ show: false, message: '', type: '' });
useEffect(() => {
if (initialData) setFormData(initialData);
}, [initialData]);
const showToast = (msg, type = 'success') => {
setToast({ show: true, message: msg, type });
setTimeout(() => setToast({ show: false, message: '', type: '' }), 3000);
};
const handleLogin = (e) => {
e.preventDefault();
if (password.trim() !== '') setIsLoggedIn(true);
};
const handleSave = async () => {
setIsSaving(true);
try {
const response = await fetch(GOOGLE_SCRIPT_URL, {
method: 'POST',
headers: { 'Content-Type': 'text/plain;charset=utf-8' },
body: JSON.stringify({ password, updates: formData })
});
const result = await response.json();
if (result.status === 'success') {
showToast(result.message, 'success');
} else {
showToast(result.message, 'error');
if (result.message.toLowerCase().includes("sandi")) {
setTimeout(() => setIsLoggedIn(false), 2000);
}
}
} catch (error) {
showToast('Gagal terhubung ke server. Pastikan URL Script benar.', 'error');
}
setIsSaving(false);
};
if (!isLoggedIn) {
return (
Admin Login
Masukkan kata sandi untuk masuk ke panel pengaturan CMS.
Kembali ke Website
);
}
return (
{toast.message}
Satu Cerita CMS
setIsLoggedIn(false)} className="text-sm text-gray-500 hover:text-red-500 font-medium">Keluar
{isSaving ? : }
{isSaving ? 'Menyimpan...' : 'Simpan Perubahan'}
Editor Konten Website
{Object.keys(formData).length === 0 ? (
Tidak ada data yang ditemukan. Pastikan URL Google Script sudah diisi dan Spreadsheet tidak kosong.
) : (
{Object.keys(formData).sort().map(key => (
{key.replace(/-/g, ' ')}
{String(formData[key]).length > 60 || key.includes('desc') ? (
))}
)}
);
};
// ==========================================
// KOMPONEN WEBSITE UTAMA
// ==========================================
const App = () => {
const [view, setView] = useState('home');
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [cmsData, setCmsData] = useState({});
const [activeTab, setActiveTab] = useState('wedding-only');
const [openFaq, setOpenFaq] = useState(null);
const sliderRef = useRef(null);
useEffect(() => {
const fetchCMS = async () => {
if (!GOOGLE_SCRIPT_URL) return;
try {
const response = await fetch(GOOGLE_SCRIPT_URL);
const data = await response.json();
setCmsData(data);
} catch (error) {
console.error("Gagal mengambil data CMS:", error);
}
};
fetchCMS();
}, []);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 50);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
const link = document.createElement('link');
link.href = 'https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Montserrat:wght@300;400;500;600&display=swap';
link.rel = 'stylesheet';
document.head.appendChild(link);
}, []);
const t = (key, fallback) => cmsData[key] !== undefined && cmsData[key] !== "" ? cmsData[key] : fallback;
if (view === 'admin') {
return setView('home')} />;
}
const themeStyle = { fontFamily: "'Montserrat', sans-serif" };
const serifStyle = { fontFamily: "'Cormorant Garamond', serif" };
const scrollSlider = (direction) => {
if (sliderRef.current) {
const scrollAmount = window.innerWidth < 768 ? 260 : 320;
sliderRef.current.scrollBy({ left: direction === 'left' ? -scrollAmount : scrollAmount, behavior: 'smooth' });
}
};
const handleWaLink = (message) => {
return `https://wa.me/6285194283636?text=${encodeURIComponent(message)}`;
};
return (
{/* Navigation */}
Satu Cerita.
setIsMobileMenuOpen(!isMobileMenuOpen)}>
{isMobileMenuOpen ? : }
{isMobileMenuOpen && (
)}
{/* Hero Section */}
{/* About Section */}
Tentang Kami
{t('about-title', 'Satu Cerita Bahagia.')}
{t('about-desc', 'Karena bagi kami, setiap pernikahan adalah satu cerita bahagia yang layak dirayakan dengan sepenuh hati. Kami hadir untuk merangkai momen berharga itu menjadi kisah yang utuh, penuh makna, dan tak terlupakan.')}
Kreatif & Personal
Disesuaikan secara eksklusif dengan perjalanan cinta Anda.
Tim Profesional
Mengeksekusi setiap detail dengan presisi dan keanggunan.
{/* Team / Crew Section */}
Tim Kami
{t('crew-title', 'Di Balik Layar Kebahagiaan')}
scrollSlider('left')} className="hidden md:flex absolute left-0 top-[55%] -translate-y-1/2 -ml-6 z-10 bg-[#FAF8F5] border border-[#EAE5DE] p-4 rounded-full shadow-lg text-[#221D18] hover:text-[#9C7449] transition-all">
scrollSlider('right')} className="hidden md:flex absolute right-0 top-[55%] -translate-y-1/2 -mr-6 z-10 bg-[#FAF8F5] border border-[#EAE5DE] p-4 rounded-full shadow-lg text-[#221D18] hover:text-[#9C7449] transition-all">
{[
{ name: t('crew-1-name', 'M. Aditya Fahrul Fauzy'), role: t('crew-1-role', 'Crew'), img: t('crew-1-img', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=500&q=80'), top: true },
{ name: t('crew-2-name', 'Nabila S. Alya'), role: t('crew-2-role', 'Crew'), img: t('crew-2-img', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=500&q=80'), top: false },
{ name: t('crew-3-name', 'Mely Ristiani S.'), role: t('crew-3-role', 'Crew'), img: t('crew-3-img', 'https://images.unsplash.com/photo-1531746020798-e6953c6e8e04?w=500&q=80'), top: true },
{ name: t('crew-4-name', 'Reka Indra Restika'), role: t('crew-4-role', 'Admin'), img: t('crew-4-img', 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=500&q=80'), top: false },
{ name: t('crew-5-name', 'Putri Dede Lestari'), role: t('crew-5-role', 'Crew'), img: t('crew-5-img', 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=500&q=80'), top: true },
{ name: t('crew-6-name', 'Syahrul'), role: t('crew-6-role', 'Crew'), img: t('crew-6-img', 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=500&q=80'), top: false },
].map((member, i) => (
{member.name}
{member.role}
))}
{/* Services (Paket Pernikahan - Full Version) */}
Layanan Kami
Paket Pernikahan
Kurasi paket elegan yang dirancang khusus untuk memenuhi impian dan kebutuhan esensial di hari bahagia Anda.
{/* Tab Navigation */}
{['wedding-only', 'all-package', 'custom'].map(tab => (
setActiveTab(tab)}
className={`px-8 py-3 rounded-full text-[11px] uppercase tracking-[0.2em] font-medium transition-all duration-300 ${activeTab === tab ? 'bg-[#221D18] text-white shadow-lg' : 'bg-transparent text-[#221D18] border border-[#221D18]/30 hover:border-[#9C7449]'}`}
>
{tab.replace('-', ' ')}
))}
{/* Tab Content: Wedding Only */}
{activeTab === 'wedding-only' && (
Paket Sweet
Rp 2.400.000
4 Crew Profesional (7 Jam)
2x Meeting & 1x Survey Lokasi
Rundown Acara
Koordinator hari-H
Pesan Sekarang
{/* Highlighted Deluxe Package */}
Paket Deluxe
Rp 4.000.000
6 Profesional Crew (8 Jam)
Master of Ceremony (MC)
1x Pendampingan Pre-Wed & Fitting
Rundown, Buku Tamu, dll.
Pesan Sekarang
Paket Luxury
Rp 4.850.000
8 Profesional Crew (8 Jam)
Master of Ceremony (MC)
1x Pendampingan Pre-Wed & Fitting
2x Meeting & 1x Survei Lokasi
Pesan Sekarang
)}
{/* Tab Content: All Package */}
{activeTab === 'all-package' && (
Paket A - Basic
Rp 25.500.000
Wedding Organizer
Dekorasi & Tenda
Makeup & Attire
Photography & MC
Pesan Sekarang
Paket B - Arsanta
Rp 37.500.000
WO & Master of Ceremony
Dekorasi & Tenda
Makeup & Attire
Photo, Video & Upacara Adat
Pesan Sekarang
Paket C - Langkara
Rp 46.800.000
Wedding Organizer
Makeup, Attire, Dekorasi
Photo & Videography
Entertainment & Adat
Pesan Sekarang
)}
{/* Tab Content: Custom */}
{activeTab === 'custom' && (
Custom Package
Mulai Rp 30.000.000
Rancang setiap detail pernikahan sesuai dengan gaya dan ekspektasi Anda. Dari pemilihan MUA, Fotografer, Dekorasi, hingga hiburan, kami memberikan kebebasan mutlak.
Mulai Konsultasi Vendor
)}
{/* Portfolio Gallery */}
Karya Kami
Galeri Kebahagiaan
Modern Tradisional
Raka & Dian
Pernikahan Intim
Andi & Sari
{/* FAQ Section */}
Pertanyaan Umum
Segala Hal yang Ingin Anda Ketahui
{[
{ q: 'Bagaimana cara kerja Paket Custom?', a: 'Anda memiliki fleksibilitas penuh untuk memilih vendor sesuai preferensi dan budget Anda. Harga total akan disesuaikan secara transparan.' },
{ q: 'Di mana lokasi operasional Satu Cerita?', a: 'Kantor pusat kami di Kuningan, Jawa Barat. Namun, kami siap melayani di berbagai venue pilihan Anda di area sekitarnya.' },
{ q: 'Berapa jumlah kru di hari H?', a: 'Jumlah kru disesuaikan dengan skala acara dan paket, mulai dari 4 kru profesional hingga 9 kru untuk memastikan kelancaran acara tanpa celah.' }
].map((faq, i) => (
setOpenFaq(openFaq === i ? null : i)} className="w-full text-left py-5 flex justify-between items-center focus:outline-none">
{faq.q}
{openFaq === i ? : }
{openFaq === i && (
{faq.a}
)}
))}
{/* Testimonial */}
"Professional banget timnya! Dari awal konsultasi sampai hari-H semuanya berjalan mulus. Hasil dekorasinya sangat elegan dan melebihi ekspektasi kami."
Raka & Dian
Pernikahan, 5 Agustus 2023
{/* CTA / Contact Section */}
{/* Footer */}
{/* Floating WA (from original HTML) */}
{/* CSS Tambahan untuk animasi */}
);
};
export default App;