Sección «Pedir evento» con gestor de reservas y calendario legible en móvil
- /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.
This commit is contained in:
parent
c3b51be992
commit
ce3127baff
38 changed files with 4029 additions and 52 deletions
152
gestor/vista.go
Normal file
152
gestor/vista.go
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FilaEvento es un evento ya resuelto para pintarlo en una celda del mes.
|
||||
type FilaEvento struct {
|
||||
Hora string
|
||||
Texto string
|
||||
Quien string
|
||||
Estado Estado
|
||||
Etiqueta string
|
||||
Sigue bool // dia intermedio o final de un evento de varios dias
|
||||
}
|
||||
|
||||
type Celda struct {
|
||||
Dia Dia
|
||||
Numero int
|
||||
Vacia bool
|
||||
Hoy bool
|
||||
Pasado bool
|
||||
Libre bool // se puede pedir: dia reservable, dentro de la ventana y sin ocupar
|
||||
Turno Turno
|
||||
EtiquetaTurno string // solo en el viernes, para no repetirla tres veces
|
||||
Eventos []FilaEvento
|
||||
}
|
||||
|
||||
type Semana struct {
|
||||
Dias []Celda
|
||||
}
|
||||
|
||||
type Mes struct {
|
||||
Nombre string
|
||||
Anio int
|
||||
Semanas []Semana
|
||||
}
|
||||
|
||||
// Vista arma los meses que se pintan, a partir del estado del registro.
|
||||
// Aqui no hay horarios semanales: esos siguen en calendario.html, que se dibuja
|
||||
// con su propio guion y no se toca.
|
||||
type Vista struct {
|
||||
reglas Reglas
|
||||
exc map[Dia]Turno
|
||||
eventos []Evento
|
||||
nombres map[string]string
|
||||
hoy Dia
|
||||
}
|
||||
|
||||
func (v Vista) eventosDe(d Dia) []FilaEvento {
|
||||
var out []FilaEvento
|
||||
for _, e := range v.eventos {
|
||||
if !e.Estado.Ocupa() || d < e.Desde || d > e.Hasta {
|
||||
continue
|
||||
}
|
||||
quien := v.nombres[e.Solicitante]
|
||||
if quien == "" {
|
||||
quien = e.Solicitante
|
||||
}
|
||||
if e.Externo() {
|
||||
quien = fmt.Sprintf("%s · amadrina %s", e.Amadrinado, quien)
|
||||
}
|
||||
out = append(out, FilaEvento{
|
||||
Hora: e.Hora,
|
||||
Texto: e.Titulo,
|
||||
Quien: quien,
|
||||
Estado: e.Estado,
|
||||
Etiqueta: e.Estado.Etiqueta(),
|
||||
Sigue: d != e.Desde,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (v Vista) celda(d Dia, numero int) Celda {
|
||||
turno := v.reglas.TurnoDe(d, v.exc)
|
||||
etiqueta := ""
|
||||
if turno != "" && d.DiaISO() == 5 {
|
||||
etiqueta = turno.Etiqueta()
|
||||
}
|
||||
primero, ultimo := v.reglas.Ventana(v.hoy)
|
||||
eventos := v.eventosDe(d)
|
||||
return Celda{
|
||||
Dia: d,
|
||||
Numero: numero,
|
||||
Hoy: d == v.hoy,
|
||||
Pasado: d < v.hoy,
|
||||
Libre: len(eventos) == 0 && d >= primero && d <= ultimo && v.reglas.reservable(d),
|
||||
Turno: turno,
|
||||
EtiquetaTurno: etiqueta,
|
||||
Eventos: eventos,
|
||||
}
|
||||
}
|
||||
|
||||
func (v Vista) Mes(anio, mes int) Mes {
|
||||
primero := fechaMes(anio, mes)
|
||||
total := diasDelMes(anio, mes)
|
||||
hueco := (int(primero.Weekday()) + 6) % 7 // celdas vacias antes del dia 1
|
||||
|
||||
m := Mes{Nombre: meses[mes-1], Anio: anio}
|
||||
semana := Semana{}
|
||||
for i := 0; i < hueco; i++ {
|
||||
semana.Dias = append(semana.Dias, Celda{Vacia: true})
|
||||
}
|
||||
for dia := 1; dia <= total; dia++ {
|
||||
d := DiaDe(primero.AddDate(0, 0, dia-1))
|
||||
semana.Dias = append(semana.Dias, v.celda(d, dia))
|
||||
if len(semana.Dias) == 7 {
|
||||
m.Semanas = append(m.Semanas, semana)
|
||||
semana = Semana{}
|
||||
}
|
||||
}
|
||||
if len(semana.Dias) > 0 {
|
||||
for len(semana.Dias) < 7 {
|
||||
semana.Dias = append(semana.Dias, Celda{Vacia: true})
|
||||
}
|
||||
m.Semanas = append(m.Semanas, semana)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Meses devuelve el mes que corre y los siguientes, que son los que se pueden
|
||||
// pedir.
|
||||
func (v Vista) Meses(cuantos int) []Mes {
|
||||
t := v.hoy.MustTime()
|
||||
var out []Mes
|
||||
for i := 0; i < cuantos; i++ {
|
||||
m := t.AddDate(0, i, 0)
|
||||
out = append(out, v.Mes(m.Year(), int(m.Month())))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Proximas lista los eventos que vienen, para la tabla de debajo del mes.
|
||||
func (v Vista) Proximas() []Evento {
|
||||
var out []Evento
|
||||
for _, e := range v.eventos {
|
||||
if e.Estado.Ocupa() && e.Hasta >= v.hoy {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func diasDelMes(anio, mes int) int {
|
||||
return fechaMes(anio, mes).AddDate(0, 1, -1).Day()
|
||||
}
|
||||
|
||||
func fechaMes(anio, mes int) time.Time {
|
||||
return time.Date(anio, time.Month(mes), 1, 0, 0, 0, 0, time.Local)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue