Formulario de contacto (SMTP vía Poste), Quiénes somos e invitación a venir al programa

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSyR5Zw5eGj6oPTRVjvP1b
This commit is contained in:
Alex
2026-10-20 16:04:00 +02:00
co-authored by Claude Sonnet 5.5
parent 4be49b0c91
commit 89bb7b4d34
9 changed files with 291 additions and 12 deletions
+80
View File
@@ -0,0 +1,80 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contacto · Anda Que No Sabes Ná</title>
<meta name="description" content="¿Quieres venir al programa, proponer un tema o decirnos algo? Escríbenos.">
<meta name="theme-color" content="#06080d">
<link rel="icon" href="img/avatar.jpg">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="top">
<a class="brand" href="/" aria-label="Anda Que No Sabes Ná, inicio"><img src="img/avatar.jpg" alt="" width="40" height="40"><span>Anda que no sabes ná</span></a>
<nav aria-label="Principal"><a href="/">Inicio</a><a href="/#quienes">Quiénes somos</a></nav>
</header>
<main class="legal contact">
<h1>¿Quieres venir al <em>programa</em>?</h1>
<p class="lead-c">Cuéntanos quién eres y de qué te gustaría hablar. También puedes proponer un tema o simplemente escribirnos. Te respondemos por correo.</p>
<form id="f" class="form" novalidate>
<label>Nombre
<input name="nombre" type="text" autocomplete="name" maxlength="100" required>
</label>
<label>Correo electrónico
<input name="email" type="email" autocomplete="email" maxlength="200" required>
</label>
<label>Motivo
<select name="motivo">
<option value="venir">Quiero venir al programa</option>
<option value="tema">Propongo un tema</option>
<option value="otro">Otro asunto</option>
</select>
</label>
<label>Mensaje
<textarea name="mensaje" rows="6" maxlength="3000" required placeholder="Cuéntanos a qué te dedicas y qué te gustaría contar…"></textarea>
</label>
<div class="trap" aria-hidden="true"><label>No rellenes este campo <input name="web" type="text" tabindex="-1" autocomplete="off"></label></div>
<label class="check">
<input name="consent" type="checkbox" required>
<span>He leído la <a href="privacidad.html" target="_blank" rel="noopener">política de privacidad</a> y acepto que usen mis datos para responderme.</span>
</label>
<button class="btn primary" type="submit">Enviar mensaje</button>
<p id="st" class="status" role="status" aria-live="polite"></p>
</form>
<noscript><p>Necesitas JavaScript para usar el formulario. Mientras tanto, escríbenos a <a href="mailto:andaquenosabesnada@gmail.com">andaquenosabesnada@gmail.com</a>.</p></noscript>
</main>
<script>
(function () {
var f = document.getElementById('f'), st = document.getElementById('st'), t0 = Date.now(), busy = false;
function say(msg, ok) { st.textContent = msg; st.className = 'status ' + (ok ? 'ok' : 'err'); }
f.addEventListener('submit', function (e) {
e.preventDefault();
if (busy) return;
var d = new FormData(f);
var body = {
nombre: (d.get('nombre') || '').trim(), email: (d.get('email') || '').trim(),
motivo: d.get('motivo'), mensaje: (d.get('mensaje') || '').trim(),
consent: f.consent.checked, web: d.get('web') || '', t: Date.now() - t0
};
if (!body.nombre || !/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(body.email) || body.mensaje.length < 10 || !body.consent) {
say('Revisa los campos: nombre, correo válido, un mensaje de al menos 10 caracteres y la casilla de privacidad.', false);
return;
}
busy = true; say('Enviando…', true);
fetch('/api/contacto', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
.then(function (r) { return r.json().catch(function () { return {}; }).then(function (j) { return { ok: r.ok && j.ok, j: j }; }); })
.then(function (x) {
if (x.ok) { f.reset(); say('¡Gracias! Hemos recibido tu mensaje y te responderemos pronto.', true); }
else { say((x.j && x.j.error) || 'No se pudo enviar. Escríbenos a andaquenosabesnada@gmail.com.', false); }
})
.catch(function () { say('No se pudo enviar. Escríbenos a andaquenosabesnada@gmail.com.', false); })
.then(function () { busy = false; });
});
})();
</script>
</body>
</html>
+21 -5
View File
@@ -21,7 +21,9 @@
<nav aria-label="Principal">
<a href="#programas">Programas</a>
<a href="#programa">El programa</a>
<a href="#quienes">Quiénes somos</a>
<a href="#seguir">Seguir</a>
<a class="navcta" href="contacto.html">Contacto</a>
</nav>
</header>
@@ -32,7 +34,7 @@
<img class="neon" src="img/avatar.jpg" alt="Anda Que No Sabes Ná" width="220" height="220">
<p class="kicker">Directo desde Granada, de noche</p>
<h1>El programa nocturno<br><em>100% granaíno</em></h1>
<p class="lead">Charlas sin guion sobre lo que sabemos, lo que no, y lo que creemos saber.</p>
<p class="lead">Charlas sobre lo que sabemos, lo que no, y lo que creemos saber.</p>
<div class="cta">
<a class="btn primary" href="https://www.youtube.com/@andaquenosabesna" target="_blank" rel="noopener">Ver en YouTube</a>
<a class="btn" href="#programas">Últimos programas</a>
@@ -121,9 +123,23 @@
<section id="programa" class="about wrap reveal">
<h2>De qué va</h2>
<p>Un programa de noche, con tertulia y sin prisa. Cada semana entra gente distinta, con su oficio y su manía, y se habla de lo que sale: de cómo se educa a un adolescente, de la vida en las calles de El Cairo, de si el árabe es tan antiguo como dicen, de qué tienen en común las religiones o de cómo se hace un truco de cartas.</p>
<ul class="chips" aria-label="Temas">
<li>Educación</li><li>Egipto</li><li>Lengua árabe</li><li>Religiones</li><li>Magia</li><li>Cultura granaína</li><li>Entrevistas</li>
</ul>
<ul class="chips" aria-label="Temas"><li>Egipto</li><li>Curiosidades</li><li>Educación</li><li>Lengua árabe</li><li>Agricultura</li><li>Burocracia</li><li>Comida egipcia</li><li>Economía</li><li>Gastronomía</li><li>Magia</li><li>Cultura granaína</li><li>Entrevistas</li></ul>
</section>
<section id="quienes" class="who wrap reveal">
<h2>Quiénes somos</h2>
<p>Somos Alejandro y Juan, las dos personas detrás de Anda Que No Sabes Ná: un programa hecho en Granada, de noche, con ganas de aprender de quien se sienta a charlar.</p>
<div class="team">
<div class="person"><b>Alejandro</b><span>Detrás de Anda Que No Sabes Ná</span></div>
<div class="person"><b>Juan</b><span>Detrás de Anda Que No Sabes Ná</span></div>
</div>
</section>
<section class="wrap reveal" aria-labelledby="venir">
<div class="invite">
<div><h2 id="venir">¿Quieres venir al programa?</h2><p>Cuéntanos quién eres y de qué te gustaría hablar.</p></div>
<a class="btn primary" href="contacto.html">Escríbenos</a>
</div>
</section>
<section id="seguir" class="follow wrap reveal">
@@ -139,7 +155,7 @@
<footer>
<div class="wrap foot">
<p>© 2026 Anda Que No Sabes Ná · Granada</p>
<p class="links"><a href="privacidad.html">Política de privacidad</a><a href="terminos.html">Condiciones del servicio</a><a href="mailto:andaquenosabesnada@gmail.com">Contacto</a></p>
<p class="links"><a href="privacidad.html">Política de privacidad</a><a href="terminos.html">Condiciones del servicio</a><a href="contacto.html">Contacto</a></p>
</div>
</footer>
+10 -6
View File
@@ -28,7 +28,11 @@
<p>Como cualquier servidor web, nuestro servidor puede registrar de forma temporal la dirección IP, la fecha y la página solicitada, únicamente por seguridad y para detectar abusos. No usamos esos registros para identificar a nadie ni los compartimos.</p>
<p>Los enlaces a YouTube, TikTok e Instagram llevan a servicios de terceros, que tienen sus propias políticas de privacidad.</p>
<h2>3. Nuestra aplicación de publicación</h2>
<h2>3. Formulario de contacto</h2>
<p>Si nos escribes desde la página de <a href="contacto.html">contacto</a>, recogemos el nombre, el correo electrónico, el motivo y el mensaje que tú mismo introduces. Los usamos <strong>únicamente para responderte</strong>. La base legal es tu consentimiento, que das al marcar la casilla del formulario.</p>
<p>La web no guarda los mensajes: el formulario los reenvía por correo electrónico, a través de nuestro propio servidor de correo, a la cuenta del programa. Los conservamos solo el tiempo necesario para atender tu consulta y no los cedemos a terceros ni los usamos para otra cosa. El formulario incluye medidas antispam que no identifican a nadie. Puedes pedirnos que borremos tus datos escribiendo al correo de contacto.</p>
<h2>4. Nuestra aplicación de publicación</h2>
<p>Usamos una aplicación de gestión de redes sociales de código abierto (Postiz), instalada en un servidor propio, para programar y publicar el contenido del programa. La aplicación está pensada para uso interno: <strong>solo la utiliza el propietario de las cuentas</strong> y no está abierta al público.</p>
<p>Para funcionar, la aplicación solicita autorización a las cuentas del propio programa y accede a:</p>
<ul>
@@ -38,21 +42,21 @@
</ul>
<p>Los tokens de acceso se guardan en una base de datos de nuestro servidor. <strong>No vendemos, alquilamos ni compartimos estos datos con nadie, no los usamos para publicidad ni los transferimos a terceros.</strong> Usamos los datos solo para publicar y programar el contenido del programa en sus propias cuentas.</p>
<h2>4. Servicios de API de YouTube</h2>
<h2>5. Servicios de API de YouTube</h2>
<p>La aplicación usa los Servicios de API de YouTube. Al utilizarla, aceptas quedar sujeto a los <a href="https://www.youtube.com/t/terms" target="_blank" rel="noopener">Términos del Servicio de YouTube</a>. La política de privacidad de Google está disponible en <a href="https://policies.google.com/privacy" target="_blank" rel="noopener">policies.google.com/privacy</a>.</p>
<p>El uso que hace la aplicación de la información recibida de las API de Google se ajusta a la <a href="https://developers.google.com/terms/api-services-user-data-policy" target="_blank" rel="noopener">Política de datos de usuario de los Servicios de API de Google</a>, incluidos los requisitos de uso limitado.</p>
<p>Puedes revocar en cualquier momento el acceso de la aplicación a tu cuenta de Google desde <a href="https://myaccount.google.com/connections" target="_blank" rel="noopener">myaccount.google.com/connections</a>.</p>
<h2>5. TikTok e Instagram</h2>
<h2>6. TikTok e Instagram</h2>
<p>El acceso a TikTok se rige también por los <a href="https://www.tiktok.com/legal/page/row/terms-of-service/es" target="_blank" rel="noopener">Términos del Servicio de TikTok</a> y su política de privacidad. El acceso a Instagram, por las condiciones y la política de datos de Meta. Puedes revocar el acceso de la aplicación desde la configuración de seguridad de cada plataforma.</p>
<h2>6. Conservación y eliminación</h2>
<h2>7. Conservación y eliminación</h2>
<p>Conservamos los tokens solo mientras la cuenta esté conectada. Si desconectamos una cuenta o revocas el acceso, el token deja de ser válido y se elimina de nuestra base de datos. Si quieres que borremos cualquier dato asociado a ti, escríbenos al correo de contacto.</p>
<h2>7. Tus derechos</h2>
<h2>8. Tus derechos</h2>
<p>Conforme al Reglamento General de Protección de Datos (RGPD) y a la normativa española, puedes ejercer tus derechos de acceso, rectificación, supresión, oposición, limitación y portabilidad escribiendo a <a href="mailto:andaquenosabesnada@gmail.com">andaquenosabesnada@gmail.com</a>. También puedes reclamar ante la Agencia Española de Protección de Datos (<a href="https://www.aepd.es" target="_blank" rel="noopener">aepd.es</a>).</p>
<h2>8. Cambios</h2>
<h2>9. Cambios</h2>
<p>Si cambiamos esta política, publicaremos la nueva versión en esta misma página con su fecha de actualización.</p>
</main>
</body>
+37
View File
@@ -39,6 +39,7 @@ a { color: inherit; text-decoration: none; }
.brand img { aspect-ratio: 1 / 1; object-fit: cover; border-radius: 50%; box-shadow: 0 0 14px rgba(46, 230, 255, .5); }
.top nav { display: flex; gap: 22px; font-size: .95rem; color: var(--muted); }
.top nav a:hover { color: var(--neon); }
.top nav .navcta { color: var(--neon); border: 1px solid rgba(46, 230, 255, .4); padding: 2px 14px; border-radius: 999px; }
@media (max-width: 640px) { .brand span { display: none; } .top nav { gap: 16px; } }
/* hero */
@@ -174,3 +175,39 @@ footer { margin-top: 40px; border-top: 1px solid var(--line); background: var(--
.legal p, .legal li { color: #c3d3e3; }
.legal a { color: var(--neon); text-decoration: underline; text-underline-offset: 3px; }
.legal .upd { color: var(--muted); font-size: .9rem; }
/* contacto */
.contact h1 em { font-style: normal; color: var(--neon); text-shadow: var(--glow); }
.lead-c { color: #c3d3e3; font-size: 1.08rem; margin-bottom: 28px; }
.form { display: grid; gap: 18px; }
.form label { display: grid; gap: 6px; font-weight: 600; font-size: .95rem; }
.form input[type=text], .form input[type=email], .form select, .form textarea {
width: 100%; padding: 13px 15px; border-radius: 12px; border: 1px solid var(--line);
background: var(--card); color: var(--ink); font: inherit; font-weight: 400; transition: .2s;
}
.form textarea { resize: vertical; min-height: 140px; }
.form input:focus, .form select:focus, .form textarea:focus { outline: none; border-color: var(--neon); box-shadow: 0 0 0 3px rgba(46, 230, 255, .18); }
.form .check { display: flex; align-items: flex-start; gap: 10px; font-weight: 400; color: var(--muted); font-size: .92rem; }
.form .check input { margin-top: 4px; accent-color: var(--neon); width: 18px; height: 18px; flex: none; }
.form .check a { color: var(--neon); text-decoration: underline; text-underline-offset: 3px; }
.form .btn { justify-self: start; cursor: pointer; font: inherit; font-weight: 700; }
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.status { min-height: 1.4em; margin: 0; font-size: .95rem; }
.status.ok { color: #6ee7a8; }
.status.err { color: #ff8a8a; }
/* quiénes somos + invitación */
.who { max-width: 900px; }
.who > p { color: #c3d3e3; font-size: 1.1rem; max-width: 720px; }
.team { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; margin-top: 26px; }
.person { padding: 22px 24px; border-radius: var(--radius); border: 1px solid var(--line); background: linear-gradient(135deg, var(--card), var(--bg2)); transition: .3s; }
.person:hover { border-color: var(--neon); box-shadow: 0 0 24px rgba(46, 230, 255, .2); }
.person b { display: block; font-size: 1.3rem; color: var(--neon); text-shadow: 0 0 14px rgba(46, 230, 255, .4); }
.person span { color: var(--muted); font-size: .95rem; }
.invite {
display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
padding: clamp(22px, 4vw, 38px); border-radius: 22px; border: 1px solid var(--line);
background: radial-gradient(80% 140% at 0% 0%, rgba(46, 230, 255, .12), transparent 60%), linear-gradient(135deg, var(--card), var(--bg2));
}
.invite h2 { margin: 0 0 4px; }
.invite p { margin: 0; color: #c3d3e3; }