// Nueva versión ampliada del proyecto inspirada en planning.wedding
// Incluye más módulos: crear sitio completo, cronograma, presupuesto, checklist, galería,
// secciones personalizadas, RSVPs avanzados, mesas, alojamiento, transporte, módulo de regalos, etc.
// --- NOTA ---
// Este es un front-end unificado en un solo archivo para demostración. En producción debe dividirse por componentes.
import React, { useState, useEffect, useRef } from "react";
import { MapContainer, TileLayer, Marker, Popup, useMapEvents } from "react-leaflet";
import L from "leaflet";
import html2canvas from "html2canvas";
import "leaflet/dist/leaflet.css";
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
});
// =============================
// Utilidad LocalStorage
// =============================
function useLocalStorage(key, initialValue) {
const [state, setState] = useState(() => {
try {
const v = localStorage.getItem(key);
return v ? JSON.parse(v) : initialValue;
} catch (e) { return initialValue; }
});
useEffect(() => {
try { localStorage.setItem(key, JSON.stringify(state)); } catch {}
}, [key, state]);
return [state, setState];
}
// =============================
// Constructor de Mapa
// =============================
function MapBuilder({ markers, setMarkers, center=[14.6349,-90.5068], zoom=13 }) {
function AddMarkerOnClick() {
useMapEvents({ click(e) {
const { lat, lng } = e.latlng;
const label = prompt("Nombre del punto");
if(label) setMarkers(m => [...m, { id: Date.now(), label, lat, lng }]);
}});
return null;
}
return (
{markers.map(m => (
{m.label}
setMarkers(cur => cur.filter(x => x.id !== m.id))}
>Eliminar
))}
);
}
// =============================
// Componente principal
// =============================
export default function EventPage() {
// ---------------- Datos principales del evento ----------------
const [event, setEvent] = useLocalStorage("event_data", {
title: "Nuestra Boda",
subtitle: "Bienvenidos a nuestro sitio oficial",
date: "2026-06-12",
time: "16:00",
locationName: "Hacienda La Esperanza",
description: "Acompáñanos a celebrar este día tan especial.",
heroImage: null,
themeColor: "pink-600",
});
// ---------------- Módulos adicionales ----------------
const [markers, setMarkers] = useLocalStorage("event_markers", []);
const [guests, setGuests] = useLocalStorage("event_guests", []);
const [checklist, setChecklist] = useLocalStorage("event_checklist", []);
const [timeline, setTimeline] = useLocalStorage("event_timeline", []);
const [budget, setBudget] = useLocalStorage("event_budget", []);
const [gallery, setGallery] = useLocalStorage("event_gallery", []);
const [lodging, setLodging] = useLocalStorage("event_lodging", []);
const [transport, setTransport] = useLocalStorage("event_transport", []);
const [gifts, setGifts] = useLocalStorage("event_gifts", []);
const [adminMode, setAdminMode] = useState(false);
const inviteRef = useRef(null);
// =============================
// Funciones
// =============================
// Invitación PNG
function downloadInvitation() {
if(!inviteRef.current) return;
html2canvas(inviteRef.current, { scale: 2 }).then(canvas => {
const a = document.createElement("a");
a.download = "invitacion.png";
a.href = canvas.toDataURL();
a.click();
});
}
// Añadir elementos genéricos
function addItemTo(setter, list, item) {
setter([...list, { id: Date.now(), ...item }]);
}
function removeItemFrom(setter, list, id) {
setter(list.filter(x => x.id !== id));
}
// =============================
// UI
// =============================
return (
{/* Header */}
{event.title}
setAdminMode(m => !m)}
>{adminMode ? "Salir Admin" : "Admin"}
{/* Hero */}
{/* Mapa */}
{/* Invitación */}
Invitación virtual
{event.title}
{event.date} — {event.time}
{event.locationName}
Descargar PNG
{/* Módulo: Invitados */}
Invitados
{guests.map(g => (
removeItemFrom(setGuests,guests,g.id)}>Borrar
))}
{adminMode && (
{
const name = prompt("Nombre");
const email = prompt("Correo");
if(name&&email) addItemTo(setGuests,guests,{name,email});
}}
className="mt-4 px-4 py-2 bg-green-600 text-white rounded"
>Añadir invitado
)}
{/* Checklist */}
Checklist
{checklist.map(c => (
{c.text}
removeItemFrom(setChecklist,checklist,c.id)}>X
))}
{adminMode && (
{
const text = prompt("Nuevo pendiente");
if(text) addItemTo(setChecklist,checklist,{text});
}} className="mt-4 px-4 py-2 bg-blue-600 text-white rounded">Añadir
)}
{/* Cronograma */}
Cronograma del Evento
{timeline.map(t => (
{t.time} — {t.activity}
removeItemFrom(setTimeline,timeline,t.id)}>X
))}
{adminMode && (
{
const time = prompt("Hora");
const activity = prompt("Actividad");
if(time&&activity) addItemTo(setTimeline,timeline,{time,activity});
}} className="mt-4 px-4 py-2 bg-purple-600 text-white rounded">Añadir actividad
)}
{/* Presupuesto */}
Presupuesto
{budget.map(b => (
{b.label}: Q{b.amount}
removeItemFrom(setBudget,budget,b.id)}>X
))}
{adminMode && (
{
const label = prompt("Concepto");
const amount = prompt("Monto");
if(label&&amount) addItemTo(setBudget,budget,{label,amount});
}} className="mt-4 px-4 py-2 bg-indigo-600 text-white rounded">Añadir gasto
)}
);
}
top of page
Proyectos Esta es la página de tu Proyecto. Es una gran oportunidad para explicar el contexto y antecedentes de tu trabajo. Haz doble clic en la caja de texto para editar, y asegúrate de agregar información relevante que quieras compartir con tus visitantes.
bottom of page