- /pedir/: calendario de fechas pedidas (negro) y confirmadas (verde), findes tranqui y con ruido, solo findes por defecto con opción de ver la semana entera. Se pincha un día libre y, sin sesión, se pide entrar y se vuelve al formulario con la fecha puesta. - Gestor en Go (gestor/): cuentas por colectivo con argon2id, sesiones y CSRF, cupo y ventana de la asamblea, caducidades, registro append-only. - La cuenta de la asamblea confirma, rechaza, cambia turnos de finde y en «Todas las fechas» quita o modifica cualquier evento. - Orden «cuenta» y «clave» con -clave y -definitiva para fijar contraseñas desde el servidor; ninguna contraseña va en el repositorio. - Botón «Pedir evento» arriba de la barra lateral en todas las páginas. - Calendario semanal en móvil: lista de días con nombre y hora de cada cita. - Fuentes servidas desde fuentes/: fuera Google Fonts en todas las páginas.
275 lines
12 KiB
HTML
275 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Calendario · La Enredadera de Tetuán</title>
|
|
<meta name="description" content="Horarios semanales de los colectivos del CSO La Enredadera de Tetuán.">
|
|
<!-- Vista previa al compartir el enlace (Mastodon, Telegram, WhatsApp). -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:site_name" content="CSO La Enredadera de Tetuán">
|
|
<meta property="og:locale" content="es_ES">
|
|
<meta property="og:title" content="Calendario · La Enredadera de Tetuán">
|
|
<meta property="og:description" content="Horarios semanales de los colectivos del CSO La Enredadera de Tetuán.">
|
|
<meta property="og:image" content="/INFO/cso_laenre.jpeg">
|
|
<meta property="og:image:alt" content="Fachada del CSO La Enredadera: el cartel «La Enredadera» pintado a mano letra a letra sobre la puerta, y encima el mural «El último tesoro que le queda a la ciudad, es el barrio»">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<link rel="preload" href="/Code462WPink-Regular.ttf" as="font" type="font/ttf" crossorigin>
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
|
<link rel="icon" href="/favicon.ico" sizes="any">
|
|
<link rel="stylesheet" href="/index.css">
|
|
|
|
</head>
|
|
<body>
|
|
|
|
<input type="checkbox" id="nav-toggle" class="sr-only">
|
|
<label for="nav-toggle" class="sb-toggle" aria-label="Abrir o cerrar el menú">☰</label>
|
|
<label for="nav-toggle" class="sb-overlay" aria-hidden="true"></label>
|
|
|
|
<div class="container">
|
|
|
|
<aside class="sidebar">
|
|
<!-- El botón va antes que el logo: es lo primero que se ve al abrir el
|
|
menú, y la asamblea lo quiso arriba del todo. -->
|
|
<a class="sb-pedir" href="/pedir/">Pedir evento</a>
|
|
<img src="/INFO/logo_enre_negro.png" alt="" class="logo" width="96" height="96">
|
|
<!-- Es la marca del panel, no un encabezado de sección: como el panel
|
|
va ANTES del <main>, un <h2> aquí hacía que el primer encabezado
|
|
del documento fuera de nivel 2 y el <h1> viniera después. -->
|
|
<p class="sb-marca">La Enredadera</p>
|
|
<p class="lema">Centro Social Autogestionado en Tetuán. Haciendo barrio desde 2009.</p>
|
|
<nav class="sidebar-nav" aria-label="Secciones">
|
|
<ul>
|
|
<li><a href="/colectivos.html">Colectivos</a></li>
|
|
<li><a href="/calendario.html" aria-current="page">Calendario</a></li>
|
|
<li><a href="/eventos.html">Eventos</a></li>
|
|
<li><a href="/subdir/campa.html">Campañas</a></li>
|
|
<li><a href="/subdir/sobre_nosotrxs.html">La Enre</a></li>
|
|
<li><a href="/subdir/contacto.html">Contacto</a></li>
|
|
<li><a href="https://okupanel.laenre.net">Actividades</a></li>
|
|
<li><a href="https://www.instagram.com/la_enredadera_de_tetuan/">Instagram</a></li>
|
|
<li><a href="https://masto.es/@csolaenre">Mastodon</a></li>
|
|
</ul>
|
|
</nav>
|
|
</aside>
|
|
|
|
<main class="main">
|
|
|
|
<header class="cabecera">
|
|
<h1>CALENDARIO</h1>
|
|
<p class="sub">Qué pasa cada semana en la Enre</p>
|
|
</header>
|
|
|
|
<a class="volver" href="/index.html">← Volver a la enredadera</a>
|
|
|
|
<section class="pantalla" aria-label="Horarios semanales de colectivos">
|
|
<h2>Horarios semanales</h2>
|
|
<p class="pie">Las asambleas y talleres fijos de cada colectivo. Pulsa cualquier etiqueta para ver los eventos publicados en convoca.la.</p>
|
|
|
|
<!-- Sin JavaScript la rejilla se queda en un marco vacío, con dos flechas
|
|
que no hacen nada. Se esconden los tres trozos que dibuja el script y
|
|
se explica dónde está la información. No toca el IIFE. -->
|
|
<noscript>
|
|
<style>
|
|
#semanal-controls, #semanal-root, #semanal-legend-root{ display:none; }
|
|
</style>
|
|
<div class="gancio-fallback">
|
|
<p><strong>El horario se dibuja con JavaScript y lo tienes desactivado.</strong></p>
|
|
<p>Los horarios de cada colectivo están también en su ficha, en
|
|
<a href="/colectivos.html">Colectivos</a>, y los eventos concretos en
|
|
<a href="/eventos.html">Eventos</a>.</p>
|
|
</div>
|
|
</noscript>
|
|
|
|
<div class="cal-controls" id="semanal-controls" role="group" aria-label="Navegación de mes">
|
|
<button class="button semanal-prev" type="button" aria-label="Mes anterior">←</button>
|
|
<div class="cal-title semanal-title" aria-live="polite"></div>
|
|
<button class="button semanal-next" type="button" aria-label="Mes siguiente">→</button>
|
|
</div>
|
|
|
|
<div class="cal" id="semanal-root"></div>
|
|
<div class="cal-legend" id="semanal-legend-root"></div>
|
|
|
|
<script>
|
|
(function () {
|
|
const monthNames = ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];
|
|
const weekdayNames = ['L','M','X','J','V','S','D'];
|
|
|
|
const ENRE_EVENTS_URL = 'https://mad.convoca.la/place/42/CSO%20La%20Enredadera';
|
|
|
|
const COLORS = {
|
|
hacklab: { bg:'#39FF14', fg:'#000', label:'Hacklab' },
|
|
artlab: { bg:'#39FF14', fg:'#ff00aa', label:'Art-Lab' },
|
|
feminismos: { bg:'#6A0DAD', fg:'#fff', label:'Feminismos' },
|
|
vivienda: { bg:'#000000', fg:'#ff00aa', label:'Vivienda' },
|
|
bicilab: { bg:'#FF7A00', fg:'#000', label:'Bicilab' },
|
|
autodefensa: { bg:'#00BCD4', fg:'#fff', label:'Autodefensa' },
|
|
fridays: { bg:'#2E8B57', fg:'#fff', label:'Fridays for Future' },
|
|
euskera: { bg:'#FF0000', fg:'#00ff00', label:'Euskera' },
|
|
asamblea_enre: { bg:'#ff00ff', fg:'#fff', label:'Asamblea Enre' },
|
|
asamblea_externas: { bg:'#fff', fg:'#ff00ff', label:'Asamblea ext.' },
|
|
montana: { bg:'#8B4513', fg:'#fff', label:'Montaña' },
|
|
teatro: { bg:'#4169E1', fg:'#fff', label:'Teatro' }
|
|
};
|
|
|
|
const WEEKLY = [
|
|
{ group:'asamblea_externas', dow:0, start:'19:00' },
|
|
{ group:'asamblea_enre', dow:0, start:'20:00' },
|
|
{ group:'autodefensa', dow:1, start:'20:00' },
|
|
{ group:'teatro', dow:2, start:'11:00' },
|
|
{ group:'autodefensa', dow:2, start:'20:00' },
|
|
{ group:'euskera', dow:2, start:'18:15' },
|
|
{ group:'vivienda', dow:3, start:'19:00' },
|
|
{ group:'feminismos', dow:3, start:'20:00' },
|
|
{ group:'bicilab', dow:6, start:'19:00' },
|
|
{ group:'montana', dow:6, start:'10:00' }
|
|
];
|
|
|
|
function createPill(text, bg, fg) {
|
|
const pill = document.createElement('div');
|
|
pill.className = 'cal-pill';
|
|
pill.style.setProperty('--bg', bg);
|
|
pill.style.setProperty('--fg', fg);
|
|
pill.textContent = text;
|
|
pill.setAttribute('role', 'link');
|
|
pill.setAttribute('tabindex', '0');
|
|
pill.addEventListener('click', () => { window.location.href = ENRE_EVENTS_URL; });
|
|
pill.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); window.location.href = ENRE_EVENTS_URL; }
|
|
});
|
|
return pill;
|
|
}
|
|
|
|
function renderWeeklyPills(cell, wd) {
|
|
const todays = WEEKLY.filter(e => e.dow === wd);
|
|
if (!todays.length) return;
|
|
const wrap = document.createElement('div');
|
|
wrap.className = 'cal-pills';
|
|
todays.forEach(e => {
|
|
const c = COLORS[e.group] || { bg:'#eee', fg:'#000', label: e.group };
|
|
wrap.appendChild(createPill(c.label + (e.start ? ' ' + e.start : ''), c.bg, c.fg));
|
|
});
|
|
cell.appendChild(wrap);
|
|
}
|
|
|
|
const root = document.getElementById('semanal-root');
|
|
const legend = document.getElementById('semanal-legend-root');
|
|
const title = document.querySelector('.semanal-title');
|
|
const btnPrev = document.querySelector('.semanal-prev');
|
|
const btnNext = document.querySelector('.semanal-next');
|
|
|
|
const today = new Date();
|
|
let view = new Date();
|
|
view.setDate(1);
|
|
|
|
function buildCalendar(date) {
|
|
root.innerHTML = '';
|
|
const year = date.getFullYear();
|
|
const month = date.getMonth();
|
|
title.textContent = monthNames[month] + ' ' + year;
|
|
|
|
const startIndex = (new Date(year, month, 1).getDay() + 6) % 7;
|
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
|
|
const fridays = [];
|
|
for (let d = 1; d <= daysInMonth; d++) {
|
|
if ((new Date(year, month, d).getDay() + 6) % 7 === 4) fridays.push(d);
|
|
}
|
|
const secondFri = fridays[1];
|
|
const lastFri = fridays[fridays.length - 1];
|
|
|
|
const head = document.createElement('div');
|
|
head.className = 'cal-row cal-head';
|
|
weekdayNames.forEach(w => {
|
|
const cell = document.createElement('div');
|
|
cell.className = 'cal-cell cal-head-cell';
|
|
cell.textContent = w;
|
|
head.appendChild(cell);
|
|
});
|
|
root.appendChild(head);
|
|
|
|
let day = 1 - startIndex;
|
|
for (let r = 0; r < 6; r++) {
|
|
const row = document.createElement('div');
|
|
row.className = 'cal-row';
|
|
for (let c = 0; c < 7; c++) {
|
|
const cell = document.createElement('div');
|
|
cell.className = 'cal-cell';
|
|
|
|
if (day >= 1 && day <= daysInMonth) {
|
|
if (year === today.getFullYear() && month === today.getMonth() && day === today.getDate())
|
|
cell.classList.add('cal-today');
|
|
|
|
const num = document.createElement('span');
|
|
num.className = 'cal-daynum';
|
|
num.textContent = day;
|
|
cell.appendChild(num);
|
|
|
|
const wd = (new Date(year, month, day).getDay() + 6) % 7;
|
|
|
|
if (wd === 4) {
|
|
const wrap = document.createElement('div');
|
|
wrap.className = 'cal-pills';
|
|
if (day === secondFri)
|
|
wrap.appendChild(createPill('Art-Lab', COLORS.artlab.bg, COLORS.artlab.fg));
|
|
else
|
|
wrap.appendChild(createPill('Hacklab', COLORS.hacklab.bg, COLORS.hacklab.fg));
|
|
if (day === secondFri || day === lastFri)
|
|
wrap.appendChild(createPill('Fridays 🌱', COLORS.fridays.bg, COLORS.fridays.fg));
|
|
cell.appendChild(wrap);
|
|
} else {
|
|
renderWeeklyPills(cell, wd);
|
|
}
|
|
} else {
|
|
cell.classList.add('cal-empty');
|
|
}
|
|
|
|
row.appendChild(cell);
|
|
day++;
|
|
}
|
|
root.appendChild(row);
|
|
}
|
|
}
|
|
|
|
function buildLegend() {
|
|
legend.innerHTML = '';
|
|
['hacklab','artlab','feminismos','vivienda','bicilab','autodefensa','fridays','euskera','asamblea_enre','montana','teatro']
|
|
.forEach(key => {
|
|
const c = COLORS[key];
|
|
if (!c) return;
|
|
const item = document.createElement('div');
|
|
item.className = 'cal-legend-item';
|
|
const dot = document.createElement('span');
|
|
dot.className = 'cal-legend-dot';
|
|
dot.style.background = c.bg;
|
|
if (c.bg === '#fff') dot.style.border = '1px solid #ff00aa';
|
|
const txt = document.createElement('span');
|
|
txt.textContent = c.label;
|
|
item.appendChild(dot);
|
|
item.appendChild(txt);
|
|
legend.appendChild(item);
|
|
});
|
|
}
|
|
|
|
btnPrev.addEventListener('click', () => { view.setMonth(view.getMonth() - 1); buildCalendar(view); });
|
|
btnNext.addEventListener('click', () => { view.setMonth(view.getMonth() + 1); buildCalendar(view); });
|
|
|
|
buildCalendar(view);
|
|
buildLegend();
|
|
})();
|
|
</script>
|
|
</section>
|
|
|
|
<footer class="pie-pagina">
|
|
<p>
|
|
Calle de la Coruña nº 5, Madrid ·
|
|
<a href="mailto:enredadera@riseup.net">enredadera@riseup.net</a> ·
|
|
<a href="https://masto.es/@csolaenre">@csolaenre</a>
|
|
</p>
|
|
</footer>
|
|
|
|
</main>
|
|
</div>
|
|
|
|
</body>
|
|
</html>
|