Copre giocatori-squadra e la scelta dei messaggi push palloni
Estrae in palloni-core.ts la logica pura di push-messaggio e promemoria-palloni (finora mista alle chiamate Supabase), così da poterla testare senza scrivere sul database. Aggiunge test per le funzioni pure di giocatori-squadra.ts, finora senza copertura. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+60
-12
@@ -1,4 +1,4 @@
|
||||
import { giocatori } from "./crapp-data";
|
||||
import { formatData, giocatori } from "./crapp-data";
|
||||
import type { Evento } from "./eventi";
|
||||
|
||||
export type Turno = { evento_id: string; giocatore_id: string; aggiornato_da: string | null };
|
||||
@@ -32,17 +32,15 @@ export function completaTurni(
|
||||
}
|
||||
if (assegnato) return;
|
||||
|
||||
const scelto = giocatori
|
||||
.slice()
|
||||
.sort((a, b) => {
|
||||
const ca = conteggio.get(a.id) ?? 0;
|
||||
const cb = conteggio.get(b.id) ?? 0;
|
||||
if (ca !== cb) return ca - cb;
|
||||
const ua = ultimo.get(a.id) ?? -1;
|
||||
const ub = ultimo.get(b.id) ?? -1;
|
||||
if (ua !== ub) return ua - ub;
|
||||
return a.nome.localeCompare(b.nome);
|
||||
})[0];
|
||||
const scelto = giocatori.slice().sort((a, b) => {
|
||||
const ca = conteggio.get(a.id) ?? 0;
|
||||
const cb = conteggio.get(b.id) ?? 0;
|
||||
if (ca !== cb) return ca - cb;
|
||||
const ua = ultimo.get(a.id) ?? -1;
|
||||
const ub = ultimo.get(b.id) ?? -1;
|
||||
if (ua !== ub) return ua - ub;
|
||||
return a.nome.localeCompare(b.nome);
|
||||
})[0];
|
||||
|
||||
if (!scelto) return;
|
||||
risultato[evento.id] = scelto.id;
|
||||
@@ -83,3 +81,53 @@ export function oggiISO(): string {
|
||||
const dd = String(d.getDate()).padStart(2, "0");
|
||||
return `${d.getFullYear()}-${mm}-${dd}`;
|
||||
}
|
||||
|
||||
/** Chi deve ricevere l'avviso push, oggi: chi porta i palloni e chi li riprende. */
|
||||
export function destinatariPromemoriaPalloni(
|
||||
turni: Record<string, string>,
|
||||
eventi: Evento[],
|
||||
oggi: string,
|
||||
): string[] {
|
||||
const destinatari = new Set<string>();
|
||||
for (const evento of eventiDelGiorno(eventi, oggi)) {
|
||||
const incaricato = turni[evento.id];
|
||||
if (incaricato) destinatari.add(incaricato);
|
||||
const prima = eventoPrecedente(eventi, evento.id);
|
||||
const precedente = prima ? turni[prima.id] : undefined;
|
||||
if (precedente) destinatari.add(precedente);
|
||||
}
|
||||
return [...destinatari];
|
||||
}
|
||||
|
||||
/** Testo del push per un giocatore: priorità a "riporta oggi", poi "tocca a te", poi generico. */
|
||||
export function messaggioPalloniOggi(
|
||||
turni: Record<string, string>,
|
||||
eventi: Evento[],
|
||||
oggi: string,
|
||||
mioId: string,
|
||||
nome: string,
|
||||
): { title: string; body: string } {
|
||||
for (const evento of eventiDelGiorno(eventi, oggi)) {
|
||||
const prima = eventoPrecedente(eventi, evento.id);
|
||||
if (prima && turni[prima.id] === mioId) {
|
||||
return {
|
||||
title: "Porta i palloni oggi",
|
||||
body: `${evento.titolo} · ${evento.ora}. I palloni li hai tu dalla volta scorsa.`,
|
||||
};
|
||||
}
|
||||
if (turni[evento.id] === mioId) {
|
||||
const dopo = eventoSuccessivo(eventi, evento.id);
|
||||
return {
|
||||
title: "Tocca a te prendere i palloni",
|
||||
body: dopo
|
||||
? `A fine ${evento.titolo} porta a casa i palloni e riportali il ${formatData(dopo.data)}.`
|
||||
: `A fine ${evento.titolo} porta a casa i palloni.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: "CrAPP · Turno palloni",
|
||||
body: nome ? `${nome}, controlla il turno palloni nel calendario.` : "Controlla il calendario.",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { completaTurni, eventiPalloni, eventoPrecedente, oggiISO } from "@/lib/palloni-core";
|
||||
import { completaTurni, destinatariPromemoriaPalloni, oggiISO } from "@/lib/palloni-core";
|
||||
import { inviaPush } from "@/lib/webpush.server";
|
||||
import { leggiEventi } from "@/lib/eventi.server";
|
||||
|
||||
@@ -18,22 +18,14 @@ export const Route = createFileRoute("/api/public/promemoria-palloni")({
|
||||
const turni = completaTurni(salvati, eventi);
|
||||
|
||||
const oggi = oggiISO();
|
||||
const destinatari = new Set<string>();
|
||||
for (const evento of eventiPalloni(eventi)) {
|
||||
if (evento.data !== oggi) continue;
|
||||
const incaricato = turni[evento.id];
|
||||
if (incaricato) destinatari.add(incaricato);
|
||||
const prima = eventoPrecedente(eventi, evento.id);
|
||||
const precedente = prima ? turni[prima.id] : undefined;
|
||||
if (precedente) destinatari.add(precedente);
|
||||
}
|
||||
const destinatari = destinatariPromemoriaPalloni(turni, eventi, oggi);
|
||||
|
||||
if (destinatari.size === 0) return Response.json({ inviate: 0 });
|
||||
if (destinatari.length === 0) return Response.json({ inviate: 0 });
|
||||
|
||||
const { data: iscrizioni } = await supabaseAdmin
|
||||
.from("push_subscriptions")
|
||||
.select("endpoint, giocatore_id")
|
||||
.in("giocatore_id", [...destinatari]);
|
||||
.in("giocatore_id", destinatari);
|
||||
|
||||
let inviate = 0;
|
||||
for (const iscrizione of iscrizioni ?? []) {
|
||||
@@ -56,4 +48,4 @@ export const Route = createFileRoute("/api/public/promemoria-palloni")({
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
import { formatData, giocatori } from "@/lib/crapp-data";
|
||||
import {
|
||||
completaTurni,
|
||||
eventiPalloni,
|
||||
eventoPrecedente,
|
||||
eventoSuccessivo,
|
||||
oggiISO,
|
||||
} from "@/lib/palloni-core";
|
||||
import { giocatori } from "@/lib/crapp-data";
|
||||
import { completaTurni, messaggioPalloniOggi, oggiISO } from "@/lib/palloni-core";
|
||||
import { leggiEventi } from "@/lib/eventi.server";
|
||||
|
||||
const schema = z.object({ endpoint: z.string().url().max(1000) });
|
||||
@@ -40,7 +34,8 @@ export const Route = createFileRoute("/api/public/push-messaggio")({
|
||||
.eq("endpoint", parsed.data.endpoint)
|
||||
.maybeSingle();
|
||||
|
||||
if (!iscrizione) return Response.json({ title: "CrAPP", body: "Controlla il turno palloni." });
|
||||
if (!iscrizione)
|
||||
return Response.json({ title: "CrAPP", body: "Controlla il turno palloni." });
|
||||
|
||||
const { data: righe } = await supabaseAdmin
|
||||
.from("turni_palloni")
|
||||
@@ -54,31 +49,8 @@ export const Route = createFileRoute("/api/public/push-messaggio")({
|
||||
const mioId = iscrizione.giocatore_id;
|
||||
const nome = giocatori.find((g) => g.id === mioId)?.nome ?? "";
|
||||
|
||||
for (const evento of eventiPalloni(eventi)) {
|
||||
if (evento.data !== oggi) continue;
|
||||
const prima = eventoPrecedente(eventi, evento.id);
|
||||
if (prima && turni[prima.id] === mioId) {
|
||||
return Response.json({
|
||||
title: "Porta i palloni oggi",
|
||||
body: `${evento.titolo} · ${evento.ora}. I palloni li hai tu dalla volta scorsa.`,
|
||||
});
|
||||
}
|
||||
if (turni[evento.id] === mioId) {
|
||||
const dopo = eventoSuccessivo(eventi, evento.id);
|
||||
return Response.json({
|
||||
title: "Tocca a te prendere i palloni",
|
||||
body: dopo
|
||||
? `A fine ${evento.titolo} porta a casa i palloni e riportali il ${formatData(dopo.data)}.`
|
||||
: `A fine ${evento.titolo} porta a casa i palloni.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
title: "CrAPP · Turno palloni",
|
||||
body: nome ? `${nome}, controlla il turno palloni nel calendario.` : "Controlla il calendario.",
|
||||
});
|
||||
return Response.json(messaggioPalloniOggi(turni, eventi, oggi, mioId, nome));
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user