{"id":308,"date":"2026-02-05T17:54:32","date_gmt":"2026-02-05T17:54:32","guid":{"rendered":"https:\/\/saca-plan.com\/inicio\/?page_id=308"},"modified":"2026-08-09T23:54:57","modified_gmt":"2026-08-09T23:54:57","slug":"portal-eventos","status":"publish","type":"page","link":"https:\/\/saca-plan.com\/inicio\/portal-eventos\/","title":{"rendered":"Portal Eventos"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"es\">\n\n<head>\n    <meta charset=\"UTF-8\" \/>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n    <title>Mis Eventos &#8211; Saca Plan<\/title>\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@supabase\/supabase-js@2\"><\/script>\n    <style>\n        .form-label {\n            @apply block text-sm font-semibold text-gray-800 mb-2 mt-4;\n        }\n\n        .form-input {\n            @apply w-full p-3 border-2 border-gray-400 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 shadow-sm;\n        }\n\n        .form-input:read-only {\n            @apply bg-gray-100 border-gray-300 cursor-not-allowed;\n        }\n\n        input[type=\"file\"].form-input {\n            @apply border-2 border-dashed border-blue-400 bg-blue-50 p-4 cursor-pointer;\n        }\n\n        input[type=\"file\"].form-input:hover {\n            @apply border-blue-600 bg-blue-100;\n        }\n\n        textarea.form-input {\n            resize: vertical;\n            min-height: 100px;\n            width: 100%;\n        }\n\n        .btn-primary {\n            @apply bg-blue-600 text-white font-semibold py-3 px-6 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed shadow-md;\n        }\n\n        .btn-secondary {\n            @apply bg-gray-300 text-gray-800 font-semibold py-3 px-6 rounded-lg hover:bg-gray-400 transition-colors shadow-md;\n        }\n\n        .btn-danger {\n            @apply bg-red-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-red-700 transition-colors shadow-md;\n        }\n\n        .badge {\n            @apply inline-block px-3 py-1 text-xs font-semibold rounded-full;\n        }\n\n        .badge-borrador {\n            @apply bg-gray-200 text-gray-700;\n        }\n\n        .badge-enviado {\n            @apply bg-blue-200 text-blue-700;\n        }\n\n        .badge-en_revision {\n            @apply bg-yellow-200 text-yellow-800;\n        }\n\n        .badge-aprobado {\n            @apply bg-green-200 text-green-800;\n        }\n\n        .badge-rechazado {\n            @apply bg-red-200 text-red-800;\n        }\n\n        .badge-publicado {\n            @apply bg-purple-200 text-purple-800;\n        }\n\n        .hidden {\n            display: none !important;\n        }\n\n        \/* Mejor visualizaci\u00f3n de placeholders *\/\n        input::placeholder,\n        textarea::placeholder {\n            color: #9CA3AF;\n            opacity: 0.7;\n        }\n\n        \/* Mejorar inputs de fecha y hora *\/\n        input[type=\"date\"],\n        input[type=\"time\"] {\n            color-scheme: light;\n        }\n\n        \/* Secciones del formulario *\/\n        .form-section {\n            transition: all 0.2s ease;\n        }\n\n        .form-section:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n        }\n\n        \/* Sobrescribir estilos de WordPress *\/\n        .portal-eventos-wrapper,\n        .portal-eventos-wrapper *,\n        .portal-eventos-wrapper>*,\n        .portal-eventos-wrapper .container,\n        .portal-eventos-wrapper #app-screen,\n        .portal-eventos-wrapper #app-screen>* {\n            max-width: none !important;\n            margin-left: 0 !important;\n            margin-right: 0 !important;\n        }\n\n        .portal-eventos-wrapper #app-screen .container.max-w-7xl,\n        .portal-eventos-wrapper nav>.container.max-w-7xl {\n            max-width: 1280px !important;\n            margin-left: auto !important;\n            margin-right: auto !important;\n        }\n\n        \/* Centrar card de login *\/\n        #auth-screen:not(.hidden) {\n            display: flex !important;\n            align-items: center !important;\n            justify-content: center !important;\n            padding: 16px !important;\n        }\n\n        #auth-screen.hidden {\n            display: none !important;\n        }\n\n        #auth-screen>div {\n            max-width: 440px !important;\n            width: 100% !important;\n            margin-left: auto !important;\n            margin-right: auto !important;\n        }\n\n        \/* Forzar que los inputs ocupen todo el ancho disponible *\/\n        .portal-eventos-wrapper .form-input,\n        .portal-eventos-wrapper input.form-input,\n        .portal-eventos-wrapper textarea.form-input,\n        .portal-eventos-wrapper select.form-input {\n            width: 100% !important;\n            max-width: 100% !important;\n        }\n\n        \/* Eliminar restricciones de WordPress para esta p\u00e1gina *\/\n        .portal-eventos-wrapper.is-layout-constrained> :where(:not(.alignleft):not(.alignright):not(.alignfull)),\n        .portal-eventos-wrapper .is-layout-constrained> :where(:not(.alignleft):not(.alignright):not(.alignfull)) {\n            max-width: none !important;\n            margin-left: 0 !important;\n            margin-right: 0 !important;\n        }\n    <\/style>\n    <script\n        src=\"https:\/\/maps.googleapis.com\/maps\/api\/js?key=AIzaSyAUHrO1Xc9to3V8La2Yv4OchGBla2ybocc&#038;libraries=places&#038;callback=initGooglePlaces&#038;loading=async\"\n        async defer><\/script>\n    <script>\n        window.__gmapsInitRequested = false;\n        window.initGooglePlaces = function () {\n            window.__gmapsInitRequested = true;\n        };\n    <\/script>\n<\/head>\n\n<body class=\"bg-gray-50 font-sans\">\n    <div class=\"portal-eventos-wrapper\">\n        <!-- Auth Screen -->\n        <div id=\"auth-screen\" class=\"min-h-screen flex items-center justify-center p-4\">\n            <div class=\"bg-white rounded-xl shadow-lg p-8 w-full max-w-md\">\n                <h1 class=\"text-2xl font-bold text-gray-800 mb-6 text-center\">Saca Plan &#8211; Mis Eventos<\/h1>\n\n                <div id=\"login-form\">\n                    <h2 class=\"text-lg font-semibold mb-4\">Iniciar Sesi\u00f3n<\/h2>\n                    <div class=\"space-y-4\">\n                        <div>\n                            <label class=\"form-label\">Email<\/label>\n                            <input type=\"email\" id=\"login-email\" class=\"form-input\" placeholder=\"tu@email.com\"\n                                required \/>\n                        <\/div>\n                        <div>\n                            <label class=\"form-label\">Contrase\u00f1a<\/label>\n                            <input type=\"password\" id=\"login-password\" class=\"form-input\" placeholder=\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\"\n                                required \/>\n                        <\/div>\n                        <button onclick=\"handleLogin()\" class=\"btn-primary w-full\">Entrar<\/button>\n\n                        <div class=\"flex items-center gap-2 my-3\">\n                            <div class=\"flex-1 h-px bg-gray-200\"><\/div>\n                            <span class=\"text-gray-400 text-sm\">o<\/span>\n                            <div class=\"flex-1 h-px bg-gray-200\"><\/div>\n                        <\/div>\n\n                        <button onclick=\"handleGoogleLogin()\"\n                            class=\"w-full flex items-center justify-center gap-3 py-2.5 px-4 border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700 font-medium text-sm transition-colors\">\n                            <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                                <path fill=\"#4285F4\"\n                                    d=\"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.875 2.684-6.615z\" \/>\n                                <path fill=\"#34A853\"\n                                    d=\"M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332C2.438 15.983 5.482 18 9 18z\" \/>\n                                <path fill=\"#FBBC05\"\n                                    d=\"M3.964 10.71c-.18-.54-.282-1.117-.282-1.71s.102-1.17.282-1.71V4.958H.957C.347 6.173 0 7.548 0 9s.348 2.827.957 4.042l3.007-2.332z\" \/>\n                                <path fill=\"#EA4335\"\n                                    d=\"M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0 5.482 0 2.438 2.017.957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z\" \/>\n                            <\/svg>\n                            Continuar con Google\n                        <\/button>\n\n                        <p class=\"text-center text-xs text-gray-400 mt-1\">\n                            <a href=\"https:\/\/saca-plan.com\/inicio\/forgot-password\/\" class=\"hover:underline\">\u00bfOlvidaste\n                                tu contrase\u00f1a?<\/a>\n                        <\/p>\n\n                        <div class=\"mt-4 pt-4 border-t border-gray-100 text-center\">\n                            <p class=\"text-sm text-gray-500 mb-3\">\u00bfA\u00fan no tienes la app? Descargala desde aqu\u00ed:<\/p>\n                            <div class=\"flex gap-2 justify-center\">\n                                <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.santi_anayav.planapp&#038;pcampaignid=web_share\"\n                                    target=\"_blank\"\n                                    class=\"flex items-center gap-2 px-3 py-2 bg-black text-white text-xs font-medium rounded-lg hover:bg-gray-800 transition-colors\">\n                                    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n                                        <path d=\"M3.18 23.76c.3.17.64.24.99.2l12.6-11.58L13.23 9l-10.05 14.76z\" \/>\n                                        <path\n                                            d=\"M20.49 10.39L17.3 8.58l-3.43 3.16 3.43 3.16 3.22-1.85c.92-.53.92-1.85-.03-2.66z\" \/>\n                                        <path\n                                            d=\"M3.18.24C2.88.07 2.54 0 2.19.04v23.92c.35.04.69-.03.99-.2L16.06 12 3.18.24z\" \/>\n                                        <path d=\"M4.17 1.03L13.23 9l3.54-3.26L4.17 1.03z\" \/>\n                                    <\/svg>\n                                    Google Play\n                                <\/a>\n                                <a href=\"https:\/\/apps.apple.com\/mx\/app\/planapp\/id6776222508\" target=\"_blank\"\n                                    class=\"flex items-center gap-2 px-3 py-2 bg-black text-white text-xs font-medium rounded-lg hover:bg-gray-800 transition-colors\">\n                                    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n                                        <path\n                                            d=\"M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z\" \/>\n                                    <\/svg>\n                                    App Store\n                                <\/a>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <div id=\"auth-message\" class=\"mt-4 p-3 rounded-lg text-sm hidden\"><\/div>\n            <\/div>\n        <\/div>\n\n        <!-- Main App -->\n        <div id=\"app-screen\" class=\"hidden min-h-screen\">\n            <nav class=\"bg-white shadow-md\">\n                <div class=\"container mx-auto px-8 py-4 flex justify-between items-center max-w-7xl\">\n                    <h1 class=\"text-xl font-bold text-gray-800\">Mis Eventos<\/h1>\n                    <div class=\"flex items-center gap-4\">\n                        <span id=\"user-email\" class=\"text-sm text-gray-600\"><\/span>\n                        <button onclick=\"handleLogout()\" class=\"text-sm text-red-600 hover:underline\">Cerrar\n                            Sesi\u00f3n<\/button>\n                    <\/div>\n                <\/div>\n            <\/nav>\n\n            <div class=\"container mx-auto p-6 max-w-7xl px-8\">\n                <!-- Tabs -->\n                <div class=\"flex gap-2 mb-6 border-b border-gray-200\">\n                    <button onclick=\"mostrarTab('lista')\"\n                        class=\"tab-btn px-6 py-3 font-medium rounded-t-lg transition-colors\" data-tab=\"lista\">Mis\n                        Eventos<\/button>\n                    <button onclick=\"mostrarTab('nuevo')\"\n                        class=\"tab-btn px-6 py-3 font-medium rounded-t-lg transition-colors\" data-tab=\"nuevo\">Nuevo\n                        Evento<\/button>\n                <\/div>\n\n                <!-- Tab: Lista de eventos -->\n                <div id=\"tab-lista\" class=\"hidden\">\n                    <div class=\"mb-6 bg-white p-4 rounded-lg shadow-md\">\n                        <label class=\"form-label mb-2\">\ud83d\udd0d Buscar eventos<\/label>\n                        <input type=\"text\" id=\"search-input\" placeholder=\"Escribe el nombre del evento...\"\n                            oninput=\"filtrarMisEventos()\" class=\"form-input\" \/>\n                    <\/div>\n                    <div id=\"lista-eventos\" class=\"space-y-4\"><\/div>\n                <\/div>\n\n                <!-- Tab: Nuevo\/Editar evento -->\n                <div id=\"tab-nuevo\" class=\"hidden\">\n                    <div class=\"bg-white rounded-xl shadow-lg p-8\">\n                        <h2 id=\"form-titulo\"\n                            class=\"text-2xl font-bold mb-6 text-gray-800 pb-4 border-b-2 border-gray-200\">\n                            Nuevo Evento<\/h2>\n\n                        <form id=\"evento-form\" class=\"space-y-6\">\n                            <input type=\"hidden\" id=\"evento-id\" \/>\n\n                            <!-- Secci\u00f3n: Informaci\u00f3n B\u00e1sica -->\n                            <div class=\"form-section bg-gray-50 p-5 rounded-lg border-2 border-gray-300 shadow-sm\">\n                                <h3 class=\"text-lg font-bold text-gray-800 mb-4 pb-2 border-b-2 border-gray-300\">\ud83d\udcdd\n                                    Informaci\u00f3n B\u00e1sica<\/h3>\n\n                                <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n                                    <div class=\"lg:col-span-2\">\n                                        <label class=\"form-label\">Nombre del evento *<\/label>\n                                        <input name=\"nombre\" id=\"nombre\" class=\"form-input\" required maxlength=\"255\"\n                                            placeholder=\"Ej: Concierto de Rock en Le\u00f3n\" \/>\n                                    <\/div>\n\n                                    <div>\n                                        <label class=\"form-label\">Intereses * <span\n                                                style=\"font-size:12px; font-weight:normal; color:#666\">(selecciona uno o\n                                                m\u00e1s)<\/span><\/label>\n                                        <div id=\"interes\" class=\"form-input\"\n                                            style=\"max-height:140px; overflow-y:auto; display:flex; flex-direction:column; gap:4px; padding:8px;\">\n                                        <\/div>\n                                    <\/div>\n                                <\/div>\n\n                                <div>\n                                    <label class=\"form-label\">Descripci\u00f3n *<\/label>\n                                    <textarea name=\"descripcion\" id=\"descripcion\" class=\"form-input\" rows=\"4\"\n                                        placeholder=\"Describe el evento, que incluye, p\u00fablico objetivo, etc.\"\n                                        required><\/textarea>\n                                <\/div>\n\n                                <div>\n                                    <label class=\"form-label\">Keywords (separadas por comas)<\/label>\n                                    <input name=\"keywords\" id=\"keywords\" class=\"form-input\"\n                                        placeholder=\"Ej: rock, musica, concierto\" \/>\n                                    <p class=\"text-xs text-gray-600 mt-1\">Se usar\u00e1n para b\u00fasqueda y recomendaciones<\/p>\n                                <\/div>\n                            <\/div>\n\n                            <!-- Secci\u00f3n: Imagen -->\n                            <div class=\"form-section bg-blue-50 p-5 rounded-lg border-2 border-blue-300 shadow-sm\">\n                                <h3 class=\"text-lg font-bold text-blue-800 mb-4 pb-2 border-b-2 border-blue-300\">\ud83d\uddbc\ufe0f\n                                    Imagen\n                                    del Evento<\/h3>\n\n                                <div>\n                                    <label class=\"form-label\">Imagen del evento<\/label>\n                                    <input type=\"file\" id=\"imagen-file\" accept=\"image\/jpeg,image\/png,image\/webp\"\n                                        class=\"form-input\" \/>\n                                    <p class=\"text-xs text-gray-600 mt-1\">\ud83d\udccc JPG, PNG o WEBP. Se comprimir\u00e1\n                                        autom\u00e1ticamente.\n                                        M\u00e1ximo 8MB.<\/p>\n                                    <div id=\"imagen-preview\" class=\"mt-4 hidden\">\n                                        <p class=\"text-sm text-gray-600 mb-2\">Vista previa:<\/p>\n                                        <img id=\"imagen-preview-img\"\n                                            class=\"h-40 w-auto object-cover rounded-lg border-2 border-gray-300\" \/>\n                                    <\/div>\n                                <\/div>\n                            <\/div>\n\n                            <!-- Secci\u00f3n: Ubicaci\u00f3n -->\n                            <div class=\"form-section bg-green-50 p-5 rounded-lg border-2 border-green-300 shadow-sm\">\n                                <h3 class=\"text-lg font-bold text-green-800 mb-4 pb-2 border-b-2 border-green-300\">\ud83d\udccd\n                                    Ubicaci\u00f3n<\/h3>\n\n                                <div>\n                                    <label class=\"form-label\">Ubicaci\u00f3n (Google Maps) *<\/label>\n                                    <input name=\"ubicacion\" id=\"ubicacion\" placeholder=\"Busca en Google Maps...\"\n                                        class=\"form-input !border-blue-600\" required \/>\n                                    <p class=\"text-xs text-gray-600 mt-1\">\ud83d\udccc Escribe y selecciona un lugar de Google\n                                        Maps\n                                    <\/p>\n                                <\/div>\n\n                                <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n                                    <div>\n                                        <label class=\"form-label\">Lugar \/ Venue<\/label>\n                                        <input name=\"lugar\" id=\"lugar\" class=\"form-input\"\n                                            placeholder=\"Ej: Teatro Manuel Doblado\" \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">Nombre espec\u00edfico del lugar donde se\n                                            realiza\n                                            el evento<\/p>\n                                    <\/div>\n\n                                    <div>\n                                        <label class=\"form-label\">Ciudad (se completa autom\u00e1ticamente)<\/label>\n                                        <input name=\"ciudad\" id=\"ciudad\" type=\"text\" class=\"form-input\" readonly \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">Se llena autom\u00e1ticamente al seleccionar la\n                                            ubicaci\u00f3n<\/p>\n                                    <\/div>\n                                <\/div>\n                            <\/div>\n\n                            <input type=\"hidden\" id=\"latitud\" name=\"latitud\" \/>\n                            <input type=\"hidden\" id=\"longitud\" name=\"longitud\" \/>\n                            <input type=\"hidden\" id=\"place_id\" name=\"place_id\" \/>\n                            <input type=\"hidden\" id=\"ciudad_key\" name=\"ciudad_key\" \/>\n                            <input type=\"hidden\" id=\"ciudad_label\" name=\"ciudad_label\" \/>\n                            <input type=\"hidden\" id=\"ciudad_place_id\" name=\"ciudad_place_id\" \/>\n\n                            <!-- Secci\u00f3n: Fechas y Horarios -->\n                            <div class=\"form-section bg-purple-50 p-5 rounded-lg border-2 border-purple-300 shadow-sm\">\n                                <h3 class=\"text-lg font-bold text-purple-800 mb-4 pb-2 border-b-2 border-purple-300\">\ud83d\udcc5\n                                    Fechas y Horarios<\/h3>\n\n                                <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n                                    <div>\n                                        <label class=\"form-label\">Fecha de inicio *<\/label>\n                                        <input name=\"fecha_inicio\" id=\"fecha_inicio\" type=\"date\" class=\"form-input\"\n                                            required \/>\n                                    <\/div>\n                                    <div>\n                                        <label class=\"form-label\">Fecha de fin<\/label>\n                                        <input name=\"fecha_fin\" id=\"fecha_fin\" type=\"date\" class=\"form-input\" \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">Opcional, para eventos de varios d\u00edas<\/p>\n                                    <\/div>\n                                <\/div>\n\n                                <div class=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n                                    <div>\n                                        <label class=\"form-label\">Hora de inicio<\/label>\n                                        <input name=\"hora\" id=\"hora\" type=\"time\" class=\"form-input\" \/>\n                                    <\/div>\n                                    <div>\n                                        <label class=\"form-label\">Hora de apertura de puerta<\/label>\n                                        <input name=\"hora_apertura_puerta\" id=\"hora_apertura_puerta\" type=\"time\"\n                                            class=\"form-input\" \/>\n                                    <\/div>\n                                    <div>\n                                        <label class=\"form-label\">Hora de cierre<\/label>\n                                        <input name=\"hora_cierre\" id=\"hora_cierre\" type=\"time\" class=\"form-input\" \/>\n                                    <\/div>\n                                <\/div>\n                            <\/div>\n\n                            <!-- Secci\u00f3n: Publicaci\u00f3n (visibilidad) -->\n                            <div class=\"form-section bg-indigo-50 p-5 rounded-lg border-2 border-indigo-300 shadow-sm\">\n                                <h3 class=\"text-lg font-bold text-indigo-800 mb-4 pb-2 border-b-2 border-indigo-300\">\ud83d\udc40\n                                    Publicaci\u00f3n<\/h3>\n\n                                <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n                                    <div>\n                                        <label class=\"form-label\">Visible desde *<\/label>\n                                        <input name=\"publicar_desde\" id=\"publicar_desde\" type=\"date\" class=\"form-input\"\n                                            required \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">\ud83d\udccc Desde qu\u00e9 d\u00eda quieres que aparezca en\n                                            la app<\/p>\n                                    <\/div>\n\n                                    <div>\n                                        <label class=\"form-label\">Visible hasta<\/label>\n                                        <input name=\"publicar_hasta\" id=\"publicar_hasta\" type=\"date\"\n                                            class=\"form-input\" \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">\ud83d\udccc Si lo dejas vac\u00edo, se usar\u00e1 la fecha\n                                            fin del evento (o inicio si no hay fin)<\/p>\n                                    <\/div>\n                                <\/div>\n\n                                <div class=\"mt-4 p-3 rounded-lg bg-white border border-indigo-200\">\n                                    <div class=\"text-sm text-gray-700\">\n                                        <b>Estimaci\u00f3n:<\/b> <span id=\"quote-dias\">&#8211;<\/span> d\u00edas \u2022 <b>Total:<\/b> <span\n                                            id=\"quote-total\">&#8211;<\/span>\n                                    <\/div>\n                                    <div class=\"text-xs text-gray-600 mt-1\" id=\"quote-rango\">\n                                        Rango de cobro: &#8211;\n                                    <\/div>\n                                    <div class=\"text-xs text-gray-500 mt-1\">\n                                        *C\u00e1lculo inclusivo: el \u00faltimo d\u00eda <b>S\u00cd<\/b> cuenta.\n                                    <\/div>\n                                <\/div>\n                            <\/div>\n\n                            <!-- Secci\u00f3n: Costo y Boletos -->\n                            <div class=\"form-section bg-yellow-50 p-5 rounded-lg border-2 border-yellow-300 shadow-sm\">\n                                <h3 class=\"text-lg font-bold text-yellow-800 mb-4 pb-2 border-b-2 border-yellow-300\">\ud83d\udcb0\n                                    Costo y Boletos<\/h3>\n\n                                <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n                                    <div>\n                                        <label class=\"form-label\">Costo \/ Precio<\/label>\n                                        <input name=\"costo\" id=\"costo\" type=\"text\" class=\"form-input\"\n                                            placeholder=\"Ej: Gratis, $100, $50-$200\" \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">Indica el costo o rango de precios<\/p>\n                                    <\/div>\n\n                                    <div>\n                                        <label class=\"form-label\">\u00bfD\u00f3nde comprar boletos?<\/label>\n                                        <input name=\"boletos\" id=\"boletos\" class=\"form-input\"\n                                            placeholder=\"Ej: Ticketmaster, taquilla del lugar, www.ejemplo.com\" \/>\n                                        <p class=\"text-xs text-gray-600 mt-1\">URL o lugar donde conseguir boletos<\/p>\n                                    <\/div>\n                                <\/div>\n                            <\/div>\n\n                            <!-- Botones de acci\u00f3n -->\n                            <div\n                                class=\"flex flex-col sm:flex-row gap-4 pt-6 border-t-2 border-gray-200 max-w-4xl mx-auto\">\n                                <button type=\"submit\" class=\"btn-primary flex-1\" id=\"btn-guardar\">\n                                    \ud83d\udcbe Guardar Borrador\n                                <\/button>\n                                <button type=\"button\" onclick=\"enviarARevision()\"\n                                    class=\"btn-primary flex-1 bg-green-600 hover:bg-green-700\" id=\"btn-enviar\">\n                                    \u2705 Enviar a Revisi\u00f3n\n                                <\/button>\n                                <button type=\"button\" onclick=\"cancelarEdicion()\" class=\"btn-secondary sm:flex-none\">\n                                    \u274c Cancelar\n                                <\/button>\n                            <\/div>\n                        <\/form>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <script>\n            const SUPABASE_URL = \"https:\/\/mipnxpjsziezxnmghtcl.supabase.co\";\n            const SUPABASE_ANON = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1pcG54cGpzemllenhubWdodGNsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDYyMTQ3OTUsImV4cCI6MjA2MTc5MDc5NX0.NgvIMIUWacovixfPR6TqUiaaO8ba3dZIwWYTbj2LoD8\";\n            const BUCKET_NAME = \"imagenes\";\n\n            const sb = supabase.createClient(SUPABASE_URL, SUPABASE_ANON);\n            let currentUser = null;\n            let misEventosGlobal = [];\n            let eventoEditando = null;\n            let autocompleteGoogle = null;\n            let PRICE_PER_DAY = null;\n            let interesesPermitidos = []; \/\/ array de ids v\u00e1lidos (como string)\n            let interesesMapNombre = {};   \/\/ id \u2192 nombre para display\n            let subcategoriasEv = {};     \/\/ keyed by interes_id\n\n            function parseKeywordsInput(raw) {\n                return String(raw || '')\n                    .split(',')\n                    .map(token => token.trim())\n                    .filter(Boolean);\n            }\n\n            function moneyMXN(n) {\n                return new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN' }).format(n);\n            }\n\n            async function cargarPrecioEvento() {\n                if (PRICE_PER_DAY !== null) return PRICE_PER_DAY;\n                try {\n                    const { data, error } = await sb.rpc('get_event_price_per_day_mxn');\n                    if (error) {\n                        console.error('\u274c Error llamando RPC get_event_price_per_day_mxn:', error);\n                        console.error('Detalles:', error.message, error.details, error.hint);\n                        PRICE_PER_DAY = null;\n                        return null;\n                    }\n\n                    if (data === null || data === undefined) {\n                        PRICE_PER_DAY = null;\n                        return null;\n                    }\n\n                    PRICE_PER_DAY = Number(data);\n                    return PRICE_PER_DAY;\n                } catch (e) {\n                    console.error('\u274c Excepci\u00f3n al cargar precio:', e);\n                    return null;\n                }\n            }\n\n            \/\/ ==================== AUTH ====================\n            async function checkAuth() {\n                const { data: { session } } = await sb.auth.getSession();\n                if (session ? session.user : false) {\n                    currentUser = session.user;\n                    mostrarApp();\n                    await manejarRetornoPagoEvento();\n                } else {\n                    mostrarAuth();\n                }\n            }\n\n            function mostrarAuth() {\n                document.getElementById('auth-screen').classList.remove('hidden');\n                document.getElementById('app-screen').classList.add('hidden');\n            }\n\n            function mostrarApp() {\n                document.getElementById('auth-screen').classList.add('hidden');\n                document.getElementById('app-screen').classList.remove('hidden');\n                document.getElementById('user-email').textContent = currentUser.email;\n                cargarIntereses();\n                cargarMisEventos();\n                mostrarTab('lista');\n            }\n\n            async function handleGoogleLogin() {\n                try {\n                    const redirectTo = window.location.origin + window.location.pathname;\n                    const { error } = await sb.auth.signInWithOAuth({\n                        provider: 'google',\n                        options: { redirectTo }\n                    });\n                    if (error) mostrarMensajeAuth(error.message, 'error');\n                } catch (e) {\n                    mostrarMensajeAuth(e.message, 'error');\n                }\n            }\n\n            async function handleLogin() {\n                const email = document.getElementById('login-email').value.trim();\n                const password = document.getElementById('login-password').value;\n\n                if (!email || !password) {\n                    mostrarMensajeAuth('Por favor completa todos los campos', 'error');\n                    return;\n                }\n\n                try {\n                    const { data, error } = await sb.auth.signInWithPassword({ email, password });\n                    if (error) throw error;\n                    currentUser = data.user;\n                    mostrarApp();\n                    await manejarRetornoPagoEvento();\n                } catch (error) {\n                    mostrarMensajeAuth(error.message, 'error');\n                }\n            }\n\n            async function handleLogout() {\n                await sb.auth.signOut();\n                currentUser = null;\n                mostrarAuth();\n            }\n\n            function mostrarMensajeAuth(mensaje, tipo) {\n                const div = document.getElementById('auth-message');\n                div.textContent = mensaje;\n                div.className = `mt-4 p-3 rounded-lg text-sm ${tipo === 'error' ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700'}`;\n                div.classList.remove('hidden');\n                setTimeout(() => div.classList.add('hidden'), 5000);\n            }\n\n            \/\/ ==================== TABS ====================\n            function mostrarTab(tab) {\n                document.querySelectorAll('.tab-btn').forEach(btn => {\n                    btn.classList.remove('bg-blue-600', 'text-white');\n                    btn.classList.add('bg-gray-200', 'text-gray-700');\n                    if (btn.dataset.tab === tab) {\n                        btn.classList.remove('bg-gray-200', 'text-gray-700');\n                        btn.classList.add('bg-blue-600', 'text-white');\n                    }\n                });\n\n                document.getElementById('tab-lista').classList.add('hidden');\n                document.getElementById('tab-nuevo').classList.add('hidden');\n                document.getElementById(`tab-${tab}`).classList.remove('hidden');\n\n                if (tab === 'lista') {\n                    cargarMisEventos();\n                } else if (tab === 'nuevo') {\n                    \/\/ \u2705 Solo resetea si NO est\u00e1s editando\n                    if (!eventoEditando) resetFormulario();\n                }\n            }\n\n            \/\/ ==================== INTERESES ====================\n            function toggleSubcatsEv(checkbox, interesId) {\n                const panel = checkbox.closest('div').querySelector(`.ev-subcats[data-interes=\"${interesId}\"]`);\n                if (!panel) return;\n                if (checkbox.checked) {\n                    panel.style.display = 'flex';\n                } else {\n                    panel.style.display = 'none';\n                    panel.querySelectorAll('input[type=\"checkbox\"]').forEach(cb => cb.checked = false);\n                }\n            }\n\n            async function cargarIntereses(selectedIds = [], selectedSubIds = []) {\n                try {\n                    const [{ data, error }, { data: subData, error: subError }] = await Promise.all([\n                        sb.from('intereses').select('id, nombre').order('orden', { ascending: true }),\n                        sb.from('subcategorias_intereses').select('id, interes_id, nombre').order('orden', { ascending: true })\n                    ]);\n\n                    if (error) throw error;\n                    if (subError) console.warn('Error al cargar subcategor\u00edas:', subError);\n\n                    const intereses = (data || []).filter(r => r.id && r.nombre);\n                    interesesPermitidos = intereses.map(r => String(r.id));\n                    interesesMapNombre = {};\n                    intereses.forEach(r => { interesesMapNombre[String(r.id)] = String(r.nombre).trim(); });\n\n                    subcategoriasEv = {};\n                    (subData || []).forEach(s => {\n                        if (!subcategoriasEv[s.interes_id]) subcategoriasEv[s.interes_id] = [];\n                        subcategoriasEv[s.interes_id].push(s);\n                    });\n\n                    const container = document.getElementById('interes');\n                    if (!container) return;\n\n                    const normIds = selectedIds.map(x => Number(x));\n                    const normSubIds = selectedSubIds.map(x => Number(x));\n\n                    container.innerHTML = intereses.map(r => {\n                        const isChecked = normIds.includes(Number(r.id));\n                        const subs = subcategoriasEv[r.id] || [];\n                        const subsHtml = subs.length === 0 ? '' : `\n                            <div class=\"ev-subcats\" data-interes=\"${r.id}\" style=\"display:${isChecked ? 'flex' : 'none'}; flex-direction:column; gap:2px; padding-left:24px; margin-top:2px;\">\n                                ${subs.map(s => `\n                                    <label style=\"display:flex; align-items:center; gap:6px; cursor:pointer; padding:1px 4px; border-radius:4px;\">\n                                        <input type=\"checkbox\" class=\"subcat-cb\" value=\"${s.id}\" ${normSubIds.includes(Number(s.id)) ? 'checked' : ''} style=\"width:13px; height:13px; cursor:pointer;\" \/>\n                                        <span style=\"font-size:13px; color:#555;\">${String(s.nombre).trim()}<\/span>\n                                    <\/label>\n                                `).join('')}\n                            <\/div>`;\n                        return `\n                            <div>\n                                <label style=\"display:flex; align-items:center; gap:8px; cursor:pointer; padding:2px 4px; border-radius:4px;\">\n                                    <input type=\"checkbox\" class=\"interes-cb\" value=\"${r.id}\" ${isChecked ? 'checked' : ''}\n                                        onchange=\"toggleSubcatsEv(this, ${r.id})\"\n                                        style=\"width:16px; height:16px; cursor:pointer;\" \/>\n                                    <span style=\"font-size:14px\">${String(r.nombre).trim()}<\/span>\n                                    ${subs.length > 0 ? `<span style=\"font-size:11px; color:#aaa;\">(${subs.length})<\/span>` : ''}\n                                <\/label>\n                                ${subsHtml}\n                            <\/div>`;\n                    }).join('');\n                } catch (error) {\n                    console.error('Error cargando intereses:', error);\n                }\n            }\n\n            function esInteresPermitido(idInteres) {\n                const valor = String(idInteres || '').trim();\n                if (!valor) return false;\n                return interesesPermitidos.includes(valor);\n            }\n\n            \/\/ ==================== MIS EVENTOS ====================\n            async function cargarMisEventos() {\n                try {\n                    const { data, error } = await sb\n                        .from('eventos_submissions')\n                        .select('*, submission_intereses(interes_id, intereses(nombre))')\n                        .eq('creado_por', currentUser.id)\n                        .order('creado_en', { ascending: false });\n\n                    if (error) throw error;\n                    misEventosGlobal = data || [];\n                    renderizarMisEventos();\n                } catch (error) {\n                    console.error('Error cargando eventos:', error);\n                    document.getElementById('lista-eventos').innerHTML =\n                        `<div class=\"bg-red-50 border border-red-200 rounded-lg p-4 text-red-700\">\n            Error al cargar eventos: ${error.message}\n          <\/div>`;\n                }\n            }\n\n            function filtrarMisEventos() {\n                const search = document.getElementById('search-input').value.toLowerCase();\n                const filtrados = misEventosGlobal.filter(e =>\n                    e.nombre.toLowerCase().includes(search)\n                );\n                renderizarMisEventos(filtrados);\n            }\n\n            function renderizarMisEventos(eventos = misEventosGlobal) {\n                const container = document.getElementById('lista-eventos');\n\n                if (!eventos || eventos.length === 0) {\n                    container.innerHTML = `\n          <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-8 text-center\">\n            <p class=\"text-gray-600 mb-4\">No tienes eventos registrados a\u00fan<\/p>\n            <button onclick=\"mostrarTab('nuevo')\" class=\"btn-primary\">Crear mi primer evento<\/button>\n          <\/div>`;\n                    return;\n                }\n\n                container.innerHTML = eventos.map(evento => {\n                    const puedeEditar = ['borrador', 'rechazado'].includes(evento.estado);\n                    const puedeEnviar = ['borrador', 'rechazado'].includes(evento.estado);\n                    const puedeEliminar = evento.estado === 'borrador';\n                    const puedePagar = (evento.estado === 'aprobado');\n                    const imagenUrl = evento.imagen_path\n                        ? `${SUPABASE_URL}\/storage\/v1\/object\/public\/${BUCKET_NAME}\/${evento.imagen_path}`\n                        : `${SUPABASE_URL}\/storage\/v1\/object\/public\/${BUCKET_NAME}\/defaults\/placeholder.jpeg`;\n\n\n                    return `\n          <div class=\"bg-white rounded-lg shadow-md p-4 flex gap-4\">\n            <img decoding=\"async\" src=\"${imagenUrl}\" class=\"w-32 h-32 object-cover rounded-lg\" alt=\"${evento.nombre}\" \/>\n            <div class=\"flex-1\">\n              <div class=\"flex justify-between items-start mb-2\">\n                <h3 class=\"text-lg font-bold text-gray-800\">${evento.nombre}<\/h3>\n                <span class=\"badge badge-${evento.estado}\">${formatearEstado(evento.estado)}<\/span>\n              <\/div>\n              <p class=\"text-sm text-gray-600 mb-1\">${(evento.submission_intereses || []).map(x => x.intereses?.nombre).filter(Boolean).join(', ') || 'Sin inter\u00e9s'}<\/p>\n              <p class=\"text-sm text-gray-600 mb-1\">\n                \ud83d\udcc5 ${formatearFecha(evento.fecha_inicio)}${evento.fecha_fin ? ' - ' + formatearFecha(evento.fecha_fin) : ''}\n              <\/p>\n              <p class=\"text-sm text-gray-600 mb-1\">\ud83d\udd52 ${formatearHora(evento.hora) || 'Sin hora'}${evento.hora_apertura_puerta ? ' \u00b7 Puertas: ' + formatearHora(evento.hora_apertura_puerta) : ''}${evento.hora_cierre ? ' \u00b7 Cierre: ' + formatearHora(evento.hora_cierre) : ''}<\/p>\n              <p class=\"text-sm text-gray-600 mb-2\">\ud83d\udccd ${evento.ciudad_label || evento.ciudad || 'Sin ubicaci\u00f3n'}<\/p>\n              ${evento.estado === 'rechazado' ? (evento.razon_rechazo ? `<p class=\"text-sm text-red-600 mb-2\">\u274c Rechazado: ${evento.razon_rechazo}<\/p>` : '') : ''}\n              <div class=\"flex gap-2 mt-3\">\n                ${puedeEditar ? `<button onclick=\"editarEvento(${evento.id})\" class=\"text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700\">Editar<\/button>` : ''}\n                ${puedeEliminar ? `<button onclick=\"eliminarEvento(${evento.id})\" class=\"text-sm bg-red-600 text-white px-3 py-1 rounded hover:bg-red-700\">Eliminar<\/button>` : ''}\n                ${puedeEnviar ? `<button onclick=\"enviarDirectoARevision(${evento.id})\" class=\"text-sm bg-green-600 text-white px-3 py-1 rounded hover:bg-green-700\">Enviar a Revisi\u00f3n<\/button>` : ''}\n                ${puedePagar ? `<button onclick=\"pagarEvento(${evento.id})\" class=\"text-sm bg-purple-600 text-white px-3 py-1 rounded hover:bg-purple-700\">\ud83d\udcb3 Pagar y publicar<\/button>` : ''}\n              <\/div>\n            <\/div>\n          <\/div>`;\n                }).join('');\n            }\n\n            function formatearEstado(estado) {\n                const estados = {\n                    'borrador': 'Borrador',\n                    'enviado': 'Enviado',\n                    'en_revision': 'En Revisi\u00f3n',\n                    'aprobado': 'Aprobado',\n                    'rechazado': 'Rechazado',\n                    'publicado': 'Publicado'\n                };\n                return estados[estado] || estado;\n            }\n\n            function formatearFecha(fecha) {\n                if (!fecha) return '';\n                return new Date(fecha).toLocaleDateString('es-MX', {\n                    year: 'numeric',\n                    month: 'short',\n                    day: 'numeric'\n                });\n            }\n\n            \/\/ ==================== FORMULARIO ====================\n            function resetFormulario() {\n                eventoEditando = null;\n                document.getElementById('form-titulo').textContent = 'Nuevo Evento';\n                document.getElementById('evento-form').reset();\n                document.getElementById('evento-id').value = '';\n                document.getElementById('imagen-preview').classList.add('hidden');\n                document.getElementById('btn-guardar').textContent = 'Guardar Borrador';\n                document.getElementById('btn-enviar').classList.remove('hidden');\n                document.getElementById('imagen-file').value = '';\n\n                \/\/ defaults de publicaci\u00f3n\n                const hoy = new Date();\n                const yyyy = hoy.getFullYear();\n                const mm = String(hoy.getMonth() + 1).padStart(2, '0');\n                const dd = String(hoy.getDate()).padStart(2, '0');\n                const hoyStr = `${yyyy}-${mm}-${dd}`;\n\n                document.getElementById('publicar_desde').value = hoyStr;\n                document.getElementById('publicar_hasta').value = '';\n\n                actualizarCotizacionUI();\n            }\n\n            async function editarEvento(eventoId) {\n                try {\n                    const { data, error } = await sb\n                        .from('eventos_submissions')\n                        .select('*, submission_intereses(interes_id), submission_subcategorias(subcategoria_id)')\n                        .eq('id', eventoId)\n                        .single();\n\n                    if (error) throw error;\n\n                    if (!['borrador', 'rechazado'].includes(data.estado)) {\n                        alert('Este evento no se puede editar en este estado.');\n                        return;\n                    }\n\n                    eventoEditando = data;\n                    document.getElementById('form-titulo').textContent = 'Editar Evento';\n                    document.getElementById('evento-id').value = data.id;\n\n                    \/\/ Llenar formulario\n                    document.getElementById('nombre').value = data.nombre || '';\n                    document.getElementById('descripcion').value = data.descripcion || '';\n                    document.getElementById('keywords').value = Array.isArray(data.keywords) ? data.keywords.join(', ') : '';\n                    await cargarIntereses(\n                        (data.submission_intereses || []).map(x => x.interes_id),\n                        (data.submission_subcategorias || []).map(x => x.subcategoria_id)\n                    );\n                    document.getElementById('ubicacion').value = data.ubicacion || '';\n                    document.getElementById('lugar').value = data.lugar || '';\n                    document.getElementById('ciudad').value = data.ciudad_label || data.ciudad || '';\n                    document.getElementById('latitud').value = data.latitud || '';\n                    document.getElementById('longitud').value = data.longitud || '';\n                    document.getElementById('place_id').value = data.place_id || '';\n                    document.getElementById('ciudad_key').value = data.ciudad_key || '';\n                    document.getElementById('ciudad_label').value = data.ciudad_label || '';\n                    document.getElementById('ciudad_place_id').value = data.ciudad_place_id || '';\n                    document.getElementById('fecha_inicio').value = data.fecha_inicio || '';\n                    document.getElementById('fecha_fin').value = data.fecha_fin || '';\n                    document.getElementById('hora').value = formatearHora(data.hora);\n                    document.getElementById('hora_apertura_puerta').value = formatearHora(data.hora_apertura_puerta);\n                    document.getElementById('hora_cierre').value = formatearHora(data.hora_cierre);\n                    document.getElementById('costo').value = data.costo || '';\n                    document.getElementById('boletos').value = data.boletos || '';\n                    document.getElementById('publicar_desde').value = data.publicar_desde || data.fecha_inicio || '';\n                    document.getElementById('publicar_hasta').value = data.publicar_hasta || data.fecha_fin || '';\n                    actualizarCotizacionUI();\n\n                    if (data.imagen_path) {\n                        const imagenUrl = `${SUPABASE_URL}\/storage\/v1\/object\/public\/${BUCKET_NAME}\/${data.imagen_path}`;\n                        document.getElementById('imagen-preview-img').src = imagenUrl;\n                        document.getElementById('imagen-preview').classList.remove('hidden');\n                    } else {\n                        document.getElementById('imagen-preview').classList.add('hidden');\n                    }\n\n                    mostrarTab('nuevo');\n                } catch (error) {\n                    alert('Error al cargar evento: ' + error.message);\n                }\n            }\n\n\n            function formatearHora(hora) {\n                if (!hora) return '';\n                return hora.substring(0, 5); \/\/ HH:MM\n            }\n\n            function cancelarEdicion() {\n                resetFormulario();\n                mostrarTab('lista');\n            }\n\n            async function eliminarEvento(eventoId) {\n                if (!confirm('\u00bfEliminar este evento borrador?')) return;\n\n                try {\n                    const { error } = await sb\n                        .from('eventos_submissions')\n                        .delete()\n                        .eq('id', eventoId)\n                        .eq('creado_por', currentUser.id);\n\n                    if (error) throw error;\n                    alert('Evento eliminado correctamente');\n                    cargarMisEventos();\n                } catch (error) {\n                    alert('Error al eliminar: ' + error.message);\n                }\n            }\n\n            async function enviarDirectoARevision(eventoId) {\n                if (!confirm('\u00bfEnviar este evento a revisi\u00f3n?')) return;\n\n                try {\n                    const { data, error } = await sb.rpc('enviar_evento_a_revision', {\n                        p_submission_id: eventoId\n                    });\n\n                    if (error) throw error;\n                    if (data !== 'OK') throw new Error(data);\n\n                    alert('\u2705 Evento enviado a revisi\u00f3n');\n                    cargarMisEventos();\n                } catch (error) {\n                    alert('Error: ' + (error.message || error));\n                }\n            }\n\n            async function guardarEventoSoloDatos() {\n                const eventoId = document.getElementById('evento-id').value;\n                const formData = new FormData(document.getElementById('evento-form'));\n\n                const fechaInicio = formData.get('fecha_inicio') || null;\n                const fechaFin = formData.get('fecha_fin') || null;\n                const publicarDesde = formData.get('publicar_desde') || null;\n                const publicarHastaCapturada = formData.get('publicar_hasta') || null;\n                const publicarHastaEfectiva = resolvePublicarHasta({\n                    publicarDesde,\n                    publicarHasta: publicarHastaCapturada,\n                    fechaInicio,\n                    fechaFin\n                });\n\n                const interesesSeleccionados = [...document.querySelectorAll('.interes-cb:checked')].map(cb => parseInt(cb.value));\n                const subcategoriasSeleccionadas = [...document.querySelectorAll('.subcat-cb:checked')].map(cb => parseInt(cb.value));\n                const data = {\n                    nombre: formData.get('nombre'),\n                    descripcion: formData.get('descripcion'),\n                    keywords: parseKeywordsInput(formData.get('keywords')),\n                    ubicacion: formData.get('ubicacion'),\n                    lugar: formData.get('lugar'),\n                    ciudad: formData.get('ciudad'), \/\/ opcional\n                    ciudad_key: formData.get('ciudad_key'),\n                    ciudad_label: formData.get('ciudad_label'),\n                    ciudad_place_id: formData.get('ciudad_place_id'),\n                    latitud: parseFloat(formData.get('latitud')) || null,\n                    longitud: parseFloat(formData.get('longitud')) || null,\n                    place_id: formData.get('place_id'),\n                    fecha_inicio: fechaInicio,\n                    fecha_fin: fechaFin,\n                    hora: formData.get('hora') || null,\n                    hora_apertura_puerta: formData.get('hora_apertura_puerta') || null,\n                    hora_cierre: formData.get('hora_cierre') || null,\n                    costo: formData.get('costo'),\n                    boletos: formData.get('boletos'),\n                    publicar_desde: publicarDesde,\n                    publicar_hasta: publicarHastaEfectiva\n                };\n\n                if (interesesSeleccionados.length === 0) {\n                    alert('\u26a0\ufe0f Inter\u00e9s requerido\\n\\nSelecciona al menos un inter\u00e9s de la lista.');\n                    throw new Error('VALIDATION');\n                }\n\n                \/\/ Validaci\u00f3n de campos obligatorios con mensajes espec\u00edficos\n                if (!data.nombre || !data.nombre.trim()) {\n                    alert('\u26a0\ufe0f Campo requerido\\n\\nEl nombre del evento es obligatorio.');\n                    throw new Error('VALIDATION');\n                }\n                if (!data.descripcion || !data.descripcion.trim()) {\n                    alert('\u26a0\ufe0f Campo requerido\\n\\nLa descripci\u00f3n del evento es obligatoria.');\n                    throw new Error('VALIDATION');\n                }\n                if (!data.ubicacion || !data.ubicacion.trim()) {\n                    alert('\u26a0\ufe0f Ubicaci\u00f3n requerida\\n\\nEscribe y selecciona una ubicaci\u00f3n v\u00e1lida del autocompletado de Google Maps.');\n                    throw new Error('VALIDATION');\n                }\n                if (!data.latitud || !data.longitud || !data.ciudad_key || !data.ciudad_label || !data.ciudad_place_id) {\n                    alert('\u26a0\ufe0f Ubicaci\u00f3n incompleta\\n\\nDebes seleccionar la ubicaci\u00f3n desde el autocompletado de Google Maps para obtener coordenadas y ciudad correctamente.\\n\\nBorra el campo y vuelve a escribir para ver las sugerencias.');\n                    throw new Error('VALIDATION');\n                }\n                if (!data.fecha_inicio) {\n                    alert('\u26a0\ufe0f Campo requerido\\n\\nLa fecha de inicio es obligatoria.');\n                    throw new Error('VALIDATION');\n                }\n                if (!data.publicar_desde) {\n                    alert('\u26a0\ufe0f Campo requerido\\n\\nLa fecha \"Visible desde\" es obligatoria.');\n                    throw new Error('VALIDATION');\n                }\n\n                if (publicarHastaCapturada) {\n                    if (data.publicar_desde) {\n                        if (publicarHastaCapturada < data.publicar_desde) {\n                            alert('\u26a0\ufe0f Fechas inv\u00e1lidas\\n\\nLa fecha \"Visible hasta\" no puede ser menor que \"Visible desde\".');\n                            throw new Error('VALIDATION');\n                        }\n                    }\n                }\n\n                \/\/ Confirmaci\u00f3n si no hay imagen\n                const fileInput = document.getElementById('imagen-file');\n                const archivoSeleccionado = fileInput.files ? fileInput.files[0] : null;\n                const tieneImagenExistente = !!(eventoEditando?.imagen_path);\n                if (!(archivoSeleccionado || tieneImagenExistente)) {\n                    const continuar = confirm('\u26a0\ufe0f Sin imagen\\n\\nEste evento no tiene imagen adjunta. Los eventos con imagen tienen mayor visibilidad.\\n\\n\u00bfDeseas continuar sin imagen?');\n                    if (!continuar) throw new Error('VALIDATION');\n                }\n\n                let submissionId = eventoId ? parseInt(eventoId) : null;\n\n                if (submissionId) {\n                    const { error } = await sb\n                        .from('eventos_submissions')\n                        .update(data)\n                        .eq('id', submissionId)\n                        .eq('creado_por', currentUser.id);\n\n                    if (error) throw error;\n                } else {\n                    data.creado_por = currentUser.id;\n                    data.estado = 'borrador'; \/\/ siempre al crear\n                    const { data: newData, error } = await sb\n                        .from('eventos_submissions')\n                        .insert([data])\n                        .select('id')\n                        .single();\n\n                    if (error) throw error;\n                    submissionId = newData.id;\n                }\n\n                \/\/ Actualizar junction table de intereses\n                await sb.from('submission_intereses').delete().eq('submission_id', submissionId);\n                const interesRows = interesesSeleccionados.map(interes_id => ({ submission_id: submissionId, interes_id }));\n                const { error: insErr } = await sb.from('submission_intereses').insert(interesRows);\n                if (insErr) throw insErr;\n\n                \/\/ Actualizar junction table de subcategor\u00edas\n                await sb.from('submission_subcategorias').delete().eq('submission_id', submissionId);\n                if (subcategoriasSeleccionadas.length > 0) {\n                    const subRows = subcategoriasSeleccionadas.map(subcategoria_id => ({ submission_id: submissionId, subcategoria_id }));\n                    const { error: subInsErr } = await sb.from('submission_subcategorias').insert(subRows);\n                    if (subInsErr) throw subInsErr;\n                }\n\n                if (archivoSeleccionado) {\n                    await subirImagen(archivoSeleccionado, submissionId);\n                }\n\n                return submissionId;\n            }\n\n            async function enviarARevisionRPC(submissionId) {\n                const { data, error } = await sb.rpc('enviar_evento_a_revision', {\n                    p_submission_id: parseInt(submissionId)\n                });\n                if (error) throw error;\n                if (data !== 'OK') throw new Error(data);\n            }\n\n            \/\/ ==================== SUBMIT FORMULARIO ====================\n            document.getElementById('evento-form').addEventListener('submit', async (e) => {\n                e.preventDefault();\n                try {\n                    await guardarEventoSoloDatos();\n                    alert('\u2705 Evento guardado como borrador');\n                    resetFormulario();\n                    mostrarTab('lista');\n                    cargarMisEventos();\n                } catch (err) {\n                    const errMsg = err ? err.message : null;\n                    if (!err || errMsg !== 'VALIDATION') alert('Error al guardar: ' + (errMsg || err));\n                }\n            });\n\n            async function enviarARevision() {\n                \/\/ 1) guarda cambios como borrador\/rechazado (sin tocar estado)\n                const submissionId = await guardarEventoSoloDatos();\n                \/\/ 2) cambia estado por RPC\n                await enviarARevisionRPC(submissionId);\n                alert('\u2705 Evento enviado a revisi\u00f3n');\n                resetFormulario();\n                mostrarTab('lista');\n                cargarMisEventos();\n            }\n\n            \/\/ ==================== SUBIR IMAGEN ====================\n            async function subirImagen(file, submissionId) {\n                try {\n                    console.log('\ud83d\udce4 Iniciando subida de imagen para submission ID:', submissionId);\n\n                    if (!submissionId) {\n                        throw new Error('No se proporcion\u00f3 submissionId');\n                    }\n\n                    if (!validarImagen(file)) {\n                        console.warn('\u26a0\ufe0f Imagen no v\u00e1lida');\n                        return;\n                    }\n\n                    \/\/ Comprimir\n                    console.log('\ud83d\udd04 Comprimiendo imagen...');\n                    const fileComprimido = await compressToJpg(file, {\n                        maxWidth: 1280,\n                        maxHeight: 1280,\n                        quality: 0.75\n                    });\n                    console.log('\u2705 Imagen comprimida:', fileComprimido.size, 'bytes');\n\n                    \/\/ Path: eventos_submissions\/<uid>\/evento-<id>.jpg\n                    const path = `eventos_submissions\/${currentUser.id}\/evento-${submissionId}.jpg`;\n                    console.log('\ud83d\udcc1 Ruta de destino:', path);\n\n                    \/\/ Subir a storage\n                    console.log('\u2b06\ufe0f Subiendo a Storage...');\n                    const { data: uploadData, error: uploadError } = await sb.storage\n                        .from(BUCKET_NAME)\n                        .upload(path, fileComprimido, {\n                            contentType: 'image\/jpeg',\n                            upsert: true\n                        });\n\n                    if (uploadError) {\n                        console.error('\u274c Error al subir a Storage:', uploadError);\n                        throw uploadError;\n                    }\n                    console.log('\u2705 Subida exitosa a Storage:', uploadData);\n\n                    \/\/ Actualizar registro con el path\n                    console.log('\ud83d\udcbe Actualizando imagen_path en la base de datos...');\n                    const { data: updateData, error: updateError } = await sb\n                        .from('eventos_submissions')\n                        .update({ imagen_path: path })\n                        .eq('id', parseInt(submissionId))\n                        .eq('creado_por', currentUser.id)\n                        .select();\n\n                    if (updateError) {\n                        console.error('\u274c Error al actualizar imagen_path:', updateError);\n                        throw new Error(`Error al actualizar imagen_path: ${updateError.message}`);\n                    }\n\n                    console.log('\u2705 imagen_path actualizado:', updateData);\n                    console.log('\ud83c\udf89 Proceso completo. Ruta guardada:', path);\n                } catch (error) {\n                    console.error('\u274c Error en subirImagen:', error);\n                    throw error; \/\/ Re-lanzar el error para que guardarEvento lo maneje\n                }\n            }\n\n            function validarImagen(file) {\n                const tipos = [\"image\/jpeg\", \"image\/png\", \"image\/webp\"];\n                if (!tipos.includes(file.type)) {\n                    alert(\"Solo se permiten im\u00e1genes JPG, PNG o WEBP\");\n                    return false;\n                }\n                const maxMB = 8;\n                if (file.size > maxMB * 1024 * 1024) {\n                    alert(`La imagen es muy grande. M\u00e1ximo ${maxMB} MB`);\n                    return false;\n                }\n                return true;\n            }\n\n            function loadImageFromFile(file) {\n                return new Promise((resolve, reject) => {\n                    const url = URL.createObjectURL(file);\n                    const img = new Image();\n                    img.onload = () => {\n                        URL.revokeObjectURL(url);\n                        resolve(img);\n                    };\n                    img.onerror = (e) => {\n                        URL.revokeObjectURL(url);\n                        reject(e);\n                    };\n                    img.src = url;\n                });\n            }\n\n            async function compressToJpg(file, { maxWidth = 1600, maxHeight = 1600, quality = 0.8 } = {}) {\n                const img = await loadImageFromFile(file);\n\n                let { width, height } = img;\n                const ratio = Math.min(maxWidth \/ width, maxHeight \/ height, 1);\n                const newW = Math.round(width * ratio);\n                const newH = Math.round(height * ratio);\n\n                const canvas = document.createElement(\"canvas\");\n                canvas.width = newW;\n                canvas.height = newH;\n                const ctx = canvas.getContext(\"2d\");\n\n                ctx.imageSmoothingEnabled = true;\n                ctx.imageSmoothingQuality = \"high\";\n                ctx.fillStyle = \"#ffffff\";\n                ctx.fillRect(0, 0, newW, newH);\n                ctx.drawImage(img, 0, 0, newW, newH);\n\n                const blob = await new Promise((resolve) => {\n                    canvas.toBlob((b) => resolve(b), \"image\/jpeg\", quality);\n                });\n\n                if (!blob) throw new Error(\"No se pudo comprimir la imagen\");\n                return new File([blob], \"imagen.jpg\", { type: \"image\/jpeg\" });\n            }\n\n            \/\/ ==================== GOOGLE PLACES ====================\n            function initGooglePlaces() {\n                const inputUbicacion = document.getElementById(\"ubicacion\");\n                const mapsPlaces = inputUbicacion\n                    ? (window.google\n                        ? (window.google.maps\n                            ? window.google.maps.places\n                            : null)\n                        : null)\n                    : null;\n\n                if (mapsPlaces) {\n                    autocompleteGoogle = new google.maps.places.Autocomplete(inputUbicacion, {\n                        fields: [\"formatted_address\", \"place_id\", \"geometry\", \"address_components\"],\n                        componentRestrictions: { country: \"mx\" }\n                    });\n\n                    \/\/ Limpiar todos los campos de ubicaci\u00f3n si el usuario edita manualmente\n                    inputUbicacion.addEventListener(\"input\", () => {\n                        document.getElementById(\"latitud\").value = \"\";\n                        document.getElementById(\"longitud\").value = \"\";\n                        document.getElementById(\"place_id\").value = \"\";\n                        document.getElementById(\"ciudad\").value = \"\";\n                        document.getElementById(\"ciudad_key\").value = \"\";\n                        document.getElementById(\"ciudad_label\").value = \"\";\n                        document.getElementById(\"ciudad_place_id\").value = \"\";\n                    });\n\n                    autocompleteGoogle.addListener(\"place_changed\", () => {\n                        const place = autocompleteGoogle.getPlace();\n                        if (!place.geometry) return;\n\n                        inputUbicacion.value = place.formatted_address || '';\n                        document.getElementById(\"latitud\").value = place.geometry.location.lat();\n                        document.getElementById(\"longitud\").value = place.geometry.location.lng();\n                        document.getElementById(\"place_id\").value = place.place_id || '';\n\n                        if (place.address_components) {\n                            const ciudadKey = buildCityKey(place.address_components);\n                            const ciudadLabel = buildCityLabel(place.address_components);\n\n                            document.getElementById(\"ciudad\").value = ciudadLabel;\n                            document.getElementById(\"ciudad_key\").value = ciudadKey;\n                            document.getElementById(\"ciudad_label\").value = ciudadLabel;\n                            document.getElementById(\"ciudad_place_id\").value = place.place_id || '';\n                        }\n                    });\n                }\n            }\n\n            function stripAccents(s) {\n                return (s || \"\").normalize(\"NFD\").replace(\/[\\u0300-\\u036f]\/g, \"\");\n            }\n\n            function slugify(s) {\n                return stripAccents(s)\n                    .toLowerCase()\n                    .trim()\n                    .replace(\/[^a-z0-9]+\/g, \"-\")\n                    .replace(\/-+\/g, \"-\")\n                    .replace(\/^-|-$\/g, \"\");\n            }\n\n            function getComponent(addressComponents, type) {\n                return (addressComponents || []).find(c => (c.types || []).includes(type)) || null;\n            }\n\n            function pickCityName(addressComponents) {\n                const city =\n                    getComponent(addressComponents, \"locality\") ||\n                    getComponent(addressComponents, \"postal_town\") ||\n                    getComponent(addressComponents, \"administrative_area_level_2\");\n                return city ? city.long_name : null;\n            }\n\n            function buildCityKey(addressComponents) {\n                const countryComp = getComponent(addressComponents, \"country\");\n                const stateComp = getComponent(addressComponents, \"administrative_area_level_1\");\n                const country = (countryComp ? countryComp.short_name : null) || \"xx\";\n                const state =\n                    (stateComp ? stateComp.short_name : null)\n                    || (stateComp ? stateComp.long_name : null)\n                    || \"xx\";\n                const cityName = pickCityName(addressComponents) || \"unknown\";\n                return `${slugify(country)}-${slugify(state)}-${slugify(cityName)}`;\n            }\n\n            function buildCityLabel(addressComponents) {\n                const cityName = pickCityName(addressComponents) || \"Ciudad\";\n                const stateComp = getComponent(addressComponents, \"administrative_area_level_1\");\n                const stateShort =\n                    (stateComp ? stateComp.short_name : null)\n                    || (stateComp ? stateComp.long_name : null)\n                    || \"\";\n                return stateShort ? `${cityName} (${stateShort})` : cityName;\n            }\n\n            \/\/ ==================== COTIZACI\u00d3N (D\u00cdAS Y TOTAL) ====================\n            function parseISODate(s) {\n                if (!s) return null;\n                const [y, m, d] = s.split('-').map(Number);\n                return new Date(Date.UTC(y, m - 1, d));\n            }\n\n            function formatISODateDisplay(isoDate) {\n                if (!isoDate) return '-';\n                const [y, m, d] = isoDate.split('-');\n                if (!y || !m || !d) return '-';\n                return `${d}\/${m}\/${y}`;\n            }\n\n            function diffDaysInclusive(aStr, bStr) {\n                const a = parseISODate(aStr);\n                const b = parseISODate(bStr);\n                if (!a || !b) return null;\n                const ms = (b - a);\n                const days = Math.floor(ms \/ (24 * 3600 * 1000)) + 1; \/\/ inclusivo\n                return days;\n            }\n\n            function resolvePublicarHasta({ publicarDesde, publicarHasta, fechaInicio, fechaFin }) {\n                if (publicarHasta) return publicarHasta;\n\n                const fallback = fechaFin || fechaInicio || publicarDesde || null;\n                if (!fallback) return null;\n\n                if (publicarDesde) {\n                    if (fallback < publicarDesde) return publicarDesde;\n                }\n                return fallback;\n            }\n\n            async function actualizarCotizacionUI() {\n                const elPubDesde = document.getElementById('publicar_desde');\n                const elPubHasta = document.getElementById('publicar_hasta');\n                const elFechaInicio = document.getElementById('fecha_inicio');\n                const elFechaFin = document.getElementById('fecha_fin');\n\n                const pubDesde = elPubDesde ? elPubDesde.value : '';\n                const pubHastaInput = elPubHasta ? elPubHasta.value : '';\n\n                const fechaInicio = elFechaInicio ? elFechaInicio.value : '';\n                const fechaFin = elFechaFin ? elFechaFin.value : '';\n                const pubHasta = resolvePublicarHasta({\n                    publicarDesde: pubDesde,\n                    publicarHasta: pubHastaInput,\n                    fechaInicio,\n                    fechaFin\n                });\n\n                const dias = diffDaysInclusive(pubDesde, pubHasta);\n                const diasValidos = Number.isFinite(dias) ? (dias > 0) : false;\n                const elQuoteRango = document.getElementById('quote-rango');\n\n                if (elQuoteRango) {\n                    if (pubDesde) {\n                        if (pubHasta) {\n                            elQuoteRango.textContent = `Rango de cobro: ${formatISODateDisplay(pubDesde)} a ${formatISODateDisplay(pubHasta)}`;\n                        } else {\n                            elQuoteRango.textContent = 'Rango de cobro: -';\n                        }\n                    } else {\n                        elQuoteRango.textContent = 'Rango de cobro: -';\n                    }\n                }\n\n                document.getElementById('quote-dias').textContent = diasValidos ? `${dias}` : '-';\n\n                if (!diasValidos) {\n                    document.getElementById('quote-total').textContent = '-';\n                    return;\n                }\n\n                const price = await cargarPrecioEvento();\n\n                if (!price) {\n                    document.getElementById('quote-total').textContent = '-';\n                    return;\n                }\n\n                const total = Math.round(dias * price * 100) \/ 100;\n                document.getElementById('quote-total').textContent = moneyMXN(total);\n            }\n\n            \/\/ ==================== PAGO PAYPAL ====================\n            async function pagarEvento(submissionId) {\n                try {\n                    const { data: sessionData } = await sb.auth.getSession();\n                    if (!sessionData || !sessionData.session) {\n                        alert('Necesitas iniciar sesi\u00f3n para pagar.');\n                        return;\n                    }\n\n                    const baseUrl = `${window.location.origin}${window.location.pathname}`;\n                    const returnUrl = `${baseUrl}?tipo=evento`;\n                    const cancelUrl = `${baseUrl}?tipo=evento&cancelado=1`;\n\n                    const { data, error } = await sb.functions.invoke('create_paypal_order_evento', {\n                        body: {\n                            submission_id: Number(submissionId),\n                            return_url: returnUrl,\n                            cancel_url: cancelUrl,\n                        },\n                    });\n\n                    if (error) {\n                        const msg =\n                            error.message ||\n                            error.details ||\n                            (error.context ? JSON.stringify(error.context) : '') ||\n                            JSON.stringify(error);\n                        throw new Error(msg);\n                    }\n\n                    \/\/ Si el backend logr\u00f3 publicar usando un pago ya capturado, no reenviar a PayPal.\n                    if (data?.status === 'OK' && data?.event_updated === true) {\n                        alert('\u2705 Este evento ya ten\u00eda un pago capturado y se public\u00f3 correctamente.');\n                        await cargarMisEventos();\n                        return;\n                    }\n\n                    if (data?.status === 'CAPTURADO_SIN_APLICAR') {\n                        const detalle = data?.event_error || data?.event_result || 'No se pudo aplicar el pago capturado.';\n                        throw new Error(`Ya existe un pago capturado para este evento, pero no se pudo publicar: ${detalle}`);\n                    }\n\n                    if (!data || !data.approve_url) {\n                        throw new Error('No lleg\u00f3 approve_url. Verifica create_paypal_order_evento.');\n                    }\n\n                    window.location.href = data.approve_url;\n                } catch (e) {\n                    console.error(e);\n                    alert(e.message || e);\n                }\n            }\n\n            async function manejarRetornoPagoEvento() {\n                const { data: s } = await sb.auth.getSession();\n                if (!s || !s.session) return;\n\n                const url = new URL(window.location.href);\n                const pagoId = url.searchParams.get('pago_id');\n                const orderId = url.searchParams.get('token');\n                const tipo = url.searchParams.get('tipo');\n\n                if (!(pagoId || orderId)) return;\n                if (tipo ? (tipo !== 'evento') : false) return;\n\n                url.searchParams.delete('pago_id');\n                url.searchParams.delete('token');\n                url.searchParams.delete('tipo');\n                window.history.replaceState({}, document.title, url.toString());\n\n                const { data, error } = await sb.functions.invoke('capture_paypal_order_evento', {\n                    body: {\n                        pago_id: pagoId ? Number(pagoId) : null,\n                        paypal_order_id: orderId || null,\n                    },\n                });\n\n                if (error) {\n                    const msg = error.message || error.details || JSON.stringify(error);\n                    alert('No se pudo confirmar el pago: ' + msg);\n                    return;\n                }\n\n                if (data ? (data.status ? (data.status !== 'OK') : false) : false) {\n                    alert('No se pudo confirmar el pago: ' + data.status);\n                    return;\n                }\n\n                if (data?.event_updated === false) {\n                    alert('Pago capturado, pero no se pudo publicar el evento: ' + (data?.event_error || data?.event_result || 'sin detalle'));\n                    return;\n                }\n\n                alert('\u2705 Pago confirmado correctamente.');\n                await cargarMisEventos();\n            }\n\n            \/\/ ==================== INIT ====================\n            \/\/ listeners para cotizaci\u00f3n\n            ['publicar_desde', 'publicar_hasta', 'fecha_inicio', 'fecha_fin'].forEach(id => {\n                const el = document.getElementById(id);\n                if (!el) return;\n                el.addEventListener('change', () => actualizarCotizacionUI());\n                el.addEventListener('input', () => actualizarCotizacionUI());\n            });\n\n            window.initGooglePlaces = initGooglePlaces;\n            if (window.__gmapsInitRequested) {\n                initGooglePlaces();\n            }\n\n            checkAuth();\n        <\/script>\n    <\/div>\n<\/body>\n\n<\/html>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Mis Eventos &#8211; Saca Plan Saca Plan &#8211; Mis Eventos Iniciar Sesi\u00f3n Email Contrase\u00f1a Entrar o Continuar con Google \u00bfOlvidaste tu contrase\u00f1a? \u00bfA\u00fan no tienes la app? Descargala desde aqu\u00ed: Google Play App Store Mis Eventos Cerrar Sesi\u00f3n Mis Eventos Nuevo Evento \ud83d\udd0d Buscar eventos Nuevo Evento \ud83d\udcdd Informaci\u00f3n B\u00e1sica Nombre del evento * Intereses [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-308","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/pages\/308","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/comments?post=308"}],"version-history":[{"count":52,"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/pages\/308\/revisions"}],"predecessor-version":[{"id":520,"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/pages\/308\/revisions\/520"}],"wp:attachment":[{"href":"https:\/\/saca-plan.com\/inicio\/wp-json\/wp\/v2\/media?parent=308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}