Mostra la stagione CSI nell'header di Campionato e divide lo storico per stagione
Il portale CSI non espone il nome della stagione: stagioneDi() la ricava dalla data della gara (l'anno cambia ad agosto), stagioneDaPartite() dà quella del campionato e raggruppaPerStagione() divide lo storico partite. Coperte da test in csi-core.test.ts. Documentato che oggi Campionato mostra 2025/26 mentre Squadra e Calendario mostrano 2026/27: il CSI non ha ancora creato le pagine della nuova stagione. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
33ed2398ab
commit
ae5d790011
5 files changed
+218
-111
No files matched your search
@@ -17,6 +17,8 @@ all'indietro.
|
|||||||
- **Notifiche in-app** — tolta la campanella in alto a destra: il numero delle notifiche è ora
|
- **Notifiche in-app** — tolta la campanella in alto a destra: il numero delle notifiche è ora
|
||||||
un pallino rosso sull'avatar del profilo. Toccando il pallino si apre l'elenco (leggi ed
|
un pallino rosso sull'avatar del profilo. Toccando il pallino si apre l'elenco (leggi ed
|
||||||
elimina), toccando l'avatar si va al profilo come prima.
|
elimina), toccando l'avatar si va al profilo come prima.
|
||||||
|
- **Campionato** — l'header riporta la stagione delle gare CSI (es. «Stagione 2025/26») e lo
|
||||||
|
storico partite è diviso per stagione.
|
||||||
|
|
||||||
## [0.9.2] - 2026-09-10
|
## [0.9.2] - 2026-09-10
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,15 @@ un `project_id`), nessun codice le chiama direttamente.
|
|||||||
`project_id` (767), `CSI_COPPA_PROJECT_ID` (848) e `team_id` (3359) sono costanti in
|
`project_id` (767), `CSI_COPPA_PROJECT_ID` (848) e `team_id` (3359) sono costanti in
|
||||||
`src/lib/csi-core.ts`.
|
`src/lib/csi-core.ts`.
|
||||||
|
|
||||||
|
**Stagione 2026/27 non ancora sul portale (verificato il 24/09/2026).** Il CSI non ha ancora
|
||||||
|
creato le pagine di campionato e Coppa 2026/27: le uniche competizioni della squadra sono
|
||||||
|
ancora `767` e `848`, con gare da novembre 2025 a maggio 2026. Per questo `/classifica`
|
||||||
|
mostra «Stagione 2025/26» mentre Squadra e Calendario, che hanno la stagione scritta nel
|
||||||
|
codice, mostrano già «Stagione 2026/27». È voluto: la pagina Campionato riporta la stagione
|
||||||
|
dei dati CSI che sta mostrando, non quella in corso per la squadra. Quando il CSI pubblica
|
||||||
|
la nuova stagione si aggiornano i due `project_id` (vedi sotto) e l'etichetta passa da sola
|
||||||
|
a 2026/27.
|
||||||
|
|
||||||
Per ritrovare questi id a ogni cambio stagione: `components/team-main.php?team_id=3359`
|
Per ritrovare questi id a ogni cambio stagione: `components/team-main.php?team_id=3359`
|
||||||
(dietro `team_details.php`) contiene una sezione "Campionati" con un link
|
(dietro `team_details.php`) contiene una sezione "Campionati" con un link
|
||||||
`league_details.php?project_id=…` per ogni competizione a cui la squadra è iscritta —
|
`league_details.php?project_id=…` per ogni competizione a cui la squadra è iscritta —
|
||||||
@@ -155,7 +164,8 @@ useCsi() → src/lib/csi.ts (React Query, staleTime 6h)
|
|||||||
↓
|
↓
|
||||||
/classifica → src/routes/classifica.tsx (tab "Classifica": Coppa sopra, Girone
|
/classifica → src/routes/classifica.tsx (tab "Classifica": Coppa sopra, Girone
|
||||||
sotto; tab "Storico partite": ogni squadra col proprio logo,
|
sotto; tab "Storico partite": ogni squadra col proprio logo,
|
||||||
chevron di dettaglio sulle gare cliccabili)
|
chevron di dettaglio sulle gare cliccabili, gare divise
|
||||||
|
per stagione; stagione anche nel sottotitolo dell'header)
|
||||||
|
|
||||||
CSI (portale, 3 endpoint)
|
CSI (portale, 3 endpoint)
|
||||||
↓ fetch server-side on-demand, cache per-partita 6 ore
|
↓ fetch server-side on-demand, cache per-partita 6 ore
|
||||||
@@ -239,6 +249,9 @@ DettaglioCsiEsteso → src/components/crapp/DettaglioCsi.tsx (formazio
|
|||||||
Il check con `CSI_LIVE=1` serve a scoprire il problema di parsing.
|
Il check con `CSI_LIVE=1` serve a scoprire il problema di parsing.
|
||||||
2. **`project_id` è legato alla stagione.** Per il 2026/27 servirà un nuovo id (vedi
|
2. **`project_id` è legato alla stagione.** Per il 2026/27 servirà un nuovo id (vedi
|
||||||
"Sorgente dati" sopra per come ritrovarlo). Oggi va aggiornato a mano in `csi-core.ts`.
|
"Sorgente dati" sopra per come ritrovarlo). Oggi va aggiornato a mano in `csi-core.ts`.
|
||||||
|
L'etichetta «Stagione 2025/26» di `/classifica` invece non va toccata: il portale non
|
||||||
|
espone il nome della stagione, quindi `stagioneDi()`/`stagioneDaPartite()` la ricavano
|
||||||
|
dalla data delle gare (l'anno cambia ad agosto) e segue da sola il nuovo `project_id`.
|
||||||
3. **La cache vive nel processo del server.** Si perde a ogni cold start e non è condivisa tra
|
3. **La cache vive nel processo del server.** Si perde a ogni cold start e non è condivisa tra
|
||||||
istanze — vale sia per `/api/public/csi` sia per la `Map` per-partita di
|
istanze — vale sia per `/api/public/csi` sia per la `Map` per-partita di
|
||||||
`/api/public/csi-partita/$id`. Sufficiente per una squadra; se serve di più, spostare i
|
`/api/public/csi-partita/$id`. Sufficiente per una squadra; se serve di più, spostare i
|
||||||
|
|||||||
@@ -21,6 +21,43 @@ export const CSI_TEAM_ID = 3359;
|
|||||||
export const CSI_GIRONE = "Girone B";
|
export const CSI_GIRONE = "Girone B";
|
||||||
export const CSI_NOME_SQUADRA = "C.R.A.P. Volley";
|
export const CSI_NOME_SQUADRA = "C.R.A.P. Volley";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stagione sportiva di una data `YYYY-MM-DD`, nel formato "2025/26". Il portale CSI non
|
||||||
|
* espone il nome della stagione (né nella classifica né nel calendario della squadra):
|
||||||
|
* si ricava dalla data della gara, con l'anno che cambia ad agosto — le gare di
|
||||||
|
* settembre-dicembre aprono la stagione, quelle di gennaio-luglio la chiudono.
|
||||||
|
*/
|
||||||
|
export function stagioneDi(dataIso: string): string {
|
||||||
|
const anno = Number(dataIso.slice(0, 4));
|
||||||
|
const mese = Number(dataIso.slice(5, 7));
|
||||||
|
const inizio = mese >= 8 ? anno : anno - 1;
|
||||||
|
return `${inizio}/${String((inizio + 1) % 100).padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stagione del campionato letto dal CSI: quella della prima gara, null se non ce ne sono. */
|
||||||
|
export function stagioneDaPartite(partite: ReadonlyArray<{ data: string }>): string | null {
|
||||||
|
const date = partite.map((p) => p.data).filter((d) => /^\d{4}-\d{2}/.test(d));
|
||||||
|
if (date.length === 0) return null;
|
||||||
|
return stagioneDi(date.reduce((a, b) => (a < b ? a : b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storico partite diviso per stagione, nell'ordine in cui arrivano: le gare scoutate in
|
||||||
|
* locale possono essere di stagioni diverse, quelle CSI sono tutte dello stesso campionato.
|
||||||
|
*/
|
||||||
|
export function raggruppaPerStagione<T extends { data: string }>(
|
||||||
|
match: readonly T[],
|
||||||
|
): Array<{ stagione: string; match: T[] }> {
|
||||||
|
const gruppi: Array<{ stagione: string; match: T[] }> = [];
|
||||||
|
for (const m of match) {
|
||||||
|
const stagione = stagioneDi(m.data);
|
||||||
|
const gruppo = gruppi.find((g) => g.stagione === stagione);
|
||||||
|
if (gruppo) gruppo.match.push(m);
|
||||||
|
else gruppi.push({ stagione, match: [m] });
|
||||||
|
}
|
||||||
|
return gruppi;
|
||||||
|
}
|
||||||
|
|
||||||
export const urlClassifica = (projectId = CSI_PROJECT_ID) =>
|
export const urlClassifica = (projectId = CSI_PROJECT_ID) =>
|
||||||
`${CSI_BASE}/components/project-sheets.php?project_id=${projectId}`;
|
`${CSI_BASE}/components/project-sheets.php?project_id=${projectId}`;
|
||||||
export const urlPartite = (teamId = CSI_TEAM_ID) =>
|
export const urlPartite = (teamId = CSI_TEAM_ID) =>
|
||||||
|
|||||||
+132
-110
@@ -7,7 +7,13 @@ import { PageHeader, TeamLogo } from "@/components/crapp/ui-bits";
|
|||||||
import { BarraSottosezioni } from "@/components/crapp/BarraSottosezioni";
|
import { BarraSottosezioni } from "@/components/crapp/BarraSottosezioni";
|
||||||
import { useScoutMatches } from "@/lib/scout-store";
|
import { useScoutMatches } from "@/lib/scout-store";
|
||||||
import { useCsi } from "@/lib/csi";
|
import { useCsi } from "@/lib/csi";
|
||||||
import { isNostraSquadra, matchDaPartitaCsi, partiteGiocate } from "@/lib/csi-core";
|
import {
|
||||||
|
isNostraSquadra,
|
||||||
|
matchDaPartitaCsi,
|
||||||
|
partiteGiocate,
|
||||||
|
raggruppaPerStagione,
|
||||||
|
stagioneDaPartite,
|
||||||
|
} from "@/lib/csi-core";
|
||||||
import { useVotiMvp, vincitoriMvp } from "@/lib/mvp-voti";
|
import { useVotiMvp, vincitoriMvp } from "@/lib/mvp-voti";
|
||||||
import { useEventi } from "@/lib/eventi";
|
import { useEventi } from "@/lib/eventi";
|
||||||
import { LogoSquadra } from "@/components/crapp/DettaglioCsi";
|
import { LogoSquadra } from "@/components/crapp/DettaglioCsi";
|
||||||
@@ -127,6 +133,8 @@ function Classifica() {
|
|||||||
|
|
||||||
const classifica = useMemo(() => csi?.classifica ?? [], [csi]);
|
const classifica = useMemo(() => csi?.classifica ?? [], [csi]);
|
||||||
const classificaCoppa = useMemo(() => csi?.classificaCoppa ?? [], [csi]);
|
const classificaCoppa = useMemo(() => csi?.classificaCoppa ?? [], [csi]);
|
||||||
|
// Tutte le gare, non solo quelle giocate: a inizio stagione il calendario c'è già.
|
||||||
|
const stagioneCsi = useMemo(() => stagioneDaPartite(csi?.partite ?? []), [csi]);
|
||||||
|
|
||||||
const mvpPerMatch = useMemo(() => vincitoriMvp(votiMvp.data ?? []), [votiMvp.data]);
|
const mvpPerMatch = useMemo(() => vincitoriMvp(votiMvp.data ?? []), [votiMvp.data]);
|
||||||
const eventoIdPerData = useMemo(
|
const eventoIdPerData = useMemo(
|
||||||
@@ -200,119 +208,127 @@ function Classifica() {
|
|||||||
Nessuna partita disponibile.
|
Nessuna partita disponibile.
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-5">
|
||||||
{tuttiMatch.map((m) => {
|
{raggruppaPerStagione(tuttiMatch).map(({ stagione, match }) => (
|
||||||
const vinta = m.setNostri > m.setLoro;
|
<section key={stagione} className="space-y-3">
|
||||||
const eventoId = eventoIdPerData.get(m.data);
|
<h3 className="px-1 text-sm font-bold text-foreground">Stagione {stagione}</h3>
|
||||||
const cliccabile = Boolean(eventoId) || !m.scout;
|
{match.map((m) => {
|
||||||
const casa = {
|
const vinta = m.setNostri > m.setLoro;
|
||||||
nome: m.casa ? NOME_NOI : m.avversario,
|
const eventoId = eventoIdPerData.get(m.data);
|
||||||
nostro: m.casa,
|
const cliccabile = Boolean(eventoId) || !m.scout;
|
||||||
};
|
const casa = {
|
||||||
const trasferta = {
|
nome: m.casa ? NOME_NOI : m.avversario,
|
||||||
nome: m.casa ? m.avversario : NOME_NOI,
|
nostro: m.casa,
|
||||||
nostro: !m.casa,
|
};
|
||||||
};
|
const trasferta = {
|
||||||
// Badge e parziali in ordine casa–ospite; il colore resta sulla vittoria CRAP.
|
nome: m.casa ? m.avversario : NOME_NOI,
|
||||||
const setCasa = m.casa ? m.setNostri : m.setLoro;
|
nostro: !m.casa,
|
||||||
const setOspite = m.casa ? m.setLoro : m.setNostri;
|
};
|
||||||
const contenuto = (
|
// Badge e parziali in ordine casa–ospite; il colore resta sulla vittoria CRAP.
|
||||||
<>
|
const setCasa = m.casa ? m.setNostri : m.setLoro;
|
||||||
<div className="flex items-center gap-2">
|
const setOspite = m.casa ? m.setLoro : m.setNostri;
|
||||||
<div className="min-w-0 flex-1 space-y-1.5">
|
const contenuto = (
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
<>
|
||||||
<LogoPartita
|
<div className="flex items-center gap-2">
|
||||||
nostro={casa.nostro}
|
<div className="min-w-0 flex-1 space-y-1.5">
|
||||||
logoAvversario={m.logoAvversario}
|
<div className="flex min-w-0 items-center gap-2">
|
||||||
avversario={m.avversario}
|
<LogoPartita
|
||||||
/>
|
nostro={casa.nostro}
|
||||||
<p className="truncate text-sm font-bold">{casa.nome}</p>
|
logoAvversario={m.logoAvversario}
|
||||||
</div>
|
avversario={m.avversario}
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
/>
|
||||||
<LogoPartita
|
<p className="truncate text-sm font-bold">{casa.nome}</p>
|
||||||
nostro={trasferta.nostro}
|
</div>
|
||||||
logoAvversario={m.logoAvversario}
|
<div className="flex min-w-0 items-center gap-2">
|
||||||
avversario={m.avversario}
|
<LogoPartita
|
||||||
/>
|
nostro={trasferta.nostro}
|
||||||
<p className="truncate text-sm font-bold">{trasferta.nome}</p>
|
logoAvversario={m.logoAvversario}
|
||||||
</div>
|
avversario={m.avversario}
|
||||||
<p className="text-xs text-muted-foreground">
|
/>
|
||||||
{formatData(m.data)} · MVP {m.mvp || "da votare"}
|
<p className="truncate text-sm font-bold">{trasferta.nome}</p>
|
||||||
{m.scout ? " · scoutata" : ""}
|
</div>
|
||||||
</p>
|
<p className="text-xs text-muted-foreground">
|
||||||
</div>
|
{formatData(m.data)} · MVP {m.mvp || "da votare"}
|
||||||
<span
|
{m.scout ? " · scoutata" : ""}
|
||||||
className={cn(
|
</p>
|
||||||
"shrink-0 rounded-xl px-2.5 py-1 font-display text-xl",
|
</div>
|
||||||
vinta
|
|
||||||
? "bg-success text-success-foreground"
|
|
||||||
: "bg-destructive text-destructive-foreground",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{setCasa}-{setOspite}
|
|
||||||
</span>
|
|
||||||
{cliccabile ? (
|
|
||||||
<ChevronRight className="h-5 w-5 shrink-0 text-muted-foreground" aria-hidden />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<div className="mt-3 flex flex-wrap items-center gap-1.5">
|
|
||||||
{m.parziali.map((p, i) => {
|
|
||||||
const puntiCasa = m.casa ? p[0] : p[1];
|
|
||||||
const puntiOspite = m.casa ? p[1] : p[0];
|
|
||||||
const setVintoDaNoi = p[0] > p[1];
|
|
||||||
return (
|
|
||||||
<span
|
<span
|
||||||
key={i}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-lg px-2 py-1 text-xs font-semibold tabular-nums",
|
"shrink-0 rounded-xl px-2.5 py-1 font-display text-xl",
|
||||||
setVintoDaNoi ? "bg-secondary" : "bg-muted text-muted-foreground",
|
vinta
|
||||||
|
? "bg-success text-success-foreground"
|
||||||
|
: "bg-destructive text-destructive-foreground",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{puntiCasa}-{puntiOspite}
|
{setCasa}-{setOspite}
|
||||||
</span>
|
</span>
|
||||||
);
|
{cliccabile ? (
|
||||||
})}
|
<ChevronRight
|
||||||
{eventoId && !m.mvp ? (
|
className="h-5 w-5 shrink-0 text-muted-foreground"
|
||||||
<span className="ml-auto inline-flex items-center gap-0.5 text-xs font-bold uppercase text-accent">
|
aria-hidden
|
||||||
Vota MVP <ChevronRight className="h-3.5 w-3.5" aria-hidden />
|
/>
|
||||||
</span>
|
) : null}
|
||||||
) : null}
|
</div>
|
||||||
</div>
|
<div className="mt-3 flex flex-wrap items-center gap-1.5">
|
||||||
</>
|
{m.parziali.map((p, i) => {
|
||||||
);
|
const puntiCasa = m.casa ? p[0] : p[1];
|
||||||
const aria = `${casa.nome} vs ${trasferta.nome}: dettaglio partita`;
|
const puntiOspite = m.casa ? p[1] : p[0];
|
||||||
if (eventoId) {
|
const setVintoDaNoi = p[0] > p[1];
|
||||||
return (
|
return (
|
||||||
<Link
|
<span
|
||||||
key={m.id}
|
key={i}
|
||||||
to="/partita/$id"
|
className={cn(
|
||||||
params={{ id: eventoId }}
|
"rounded-lg px-2 py-1 text-xs font-semibold tabular-nums",
|
||||||
aria-label={aria}
|
setVintoDaNoi ? "bg-secondary" : "bg-muted text-muted-foreground",
|
||||||
className="premi block rounded-3xl bg-card p-4 shadow-card ring-1 ring-transparent transition-[box-shadow,transform] active:scale-[0.99] hover:ring-accent/30"
|
)}
|
||||||
>
|
>
|
||||||
{contenuto}
|
{puntiCasa}-{puntiOspite}
|
||||||
</Link>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
})}
|
||||||
// Senza evento CrAPP collegato (nessuna creazione automatica dal calendario
|
{eventoId && !m.mvp ? (
|
||||||
// CSI, vedi "Evoluzioni possibili"): le gare scoutate localmente non hanno un
|
<span className="ml-auto inline-flex items-center gap-0.5 text-xs font-bold uppercase text-accent">
|
||||||
// corrispettivo sul portale da mostrare, quelle CSI sì.
|
Vota MVP <ChevronRight className="h-3.5 w-3.5" aria-hidden />
|
||||||
return m.scout ? (
|
</span>
|
||||||
<article key={m.id} className="rounded-3xl bg-card p-4 shadow-card">
|
) : null}
|
||||||
{contenuto}
|
</div>
|
||||||
</article>
|
</>
|
||||||
) : (
|
);
|
||||||
<Link
|
const aria = `${casa.nome} vs ${trasferta.nome}: dettaglio partita`;
|
||||||
key={m.id}
|
if (eventoId) {
|
||||||
to="/partita-csi/$id"
|
return (
|
||||||
params={{ id: m.id }}
|
<Link
|
||||||
aria-label={aria}
|
key={m.id}
|
||||||
className="premi block rounded-3xl bg-card p-4 shadow-card ring-1 ring-transparent transition-[box-shadow,transform] active:scale-[0.99] hover:ring-accent/30"
|
to="/partita/$id"
|
||||||
>
|
params={{ id: eventoId }}
|
||||||
{contenuto}
|
aria-label={aria}
|
||||||
</Link>
|
className="premi block rounded-3xl bg-card p-4 shadow-card ring-1 ring-transparent transition-[box-shadow,transform] active:scale-[0.99] hover:ring-accent/30"
|
||||||
);
|
>
|
||||||
})}
|
{contenuto}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Senza evento CrAPP collegato (nessuna creazione automatica dal calendario
|
||||||
|
// CSI, vedi "Evoluzioni possibili"): le gare scoutate localmente non hanno un
|
||||||
|
// corrispettivo sul portale da mostrare, quelle CSI sì.
|
||||||
|
return m.scout ? (
|
||||||
|
<article key={m.id} className="rounded-3xl bg-card p-4 shadow-card">
|
||||||
|
{contenuto}
|
||||||
|
</article>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
key={m.id}
|
||||||
|
to="/partita-csi/$id"
|
||||||
|
params={{ id: m.id }}
|
||||||
|
aria-label={aria}
|
||||||
|
className="premi block rounded-3xl bg-card p-4 shadow-card ring-1 ring-transparent transition-[box-shadow,transform] active:scale-[0.99] hover:ring-accent/30"
|
||||||
|
>
|
||||||
|
{contenuto}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
[tuttiMatch, eventoIdPerData],
|
[tuttiMatch, eventoIdPerData],
|
||||||
@@ -322,7 +338,13 @@ function Classifica() {
|
|||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
titolo="Campionato"
|
titolo="Campionato"
|
||||||
sottotitolo={csi ? `${csi.girone} · CSI Bologna` : "CSI Bologna"}
|
sottotitolo={
|
||||||
|
csi
|
||||||
|
? [csi.girone, "CSI Bologna", stagioneCsi && `Stagione ${stagioneCsi}`]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" · ")
|
||||||
|
: "CSI Bologna"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BarraSottosezioni
|
<BarraSottosezioni
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import {
|
|||||||
parseClassifica,
|
parseClassifica,
|
||||||
parseFormazioni,
|
parseFormazioni,
|
||||||
parseInfoPartita,
|
parseInfoPartita,
|
||||||
|
raggruppaPerStagione,
|
||||||
|
stagioneDaPartite,
|
||||||
|
stagioneDi,
|
||||||
parsePrecedenti,
|
parsePrecedenti,
|
||||||
partiteDaEventi,
|
partiteDaEventi,
|
||||||
partiteFormatoSospetto,
|
partiteFormatoSospetto,
|
||||||
@@ -368,4 +371,34 @@ if (process.env["CSI_LIVE"]) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- stagioneDi / stagioneDaPartite: l'anno cambia ad agosto ------------------
|
||||||
|
assert.equal(stagioneDi("2025-11-12"), "2025/26");
|
||||||
|
assert.equal(stagioneDi("2026-05-20"), "2025/26");
|
||||||
|
assert.equal(stagioneDi("2026-07-31"), "2025/26");
|
||||||
|
assert.equal(stagioneDi("2026-08-01"), "2026/27");
|
||||||
|
assert.equal(stagioneDi("2099-10-01"), "2099/00");
|
||||||
|
assert.equal(stagioneDaPartite([]), null);
|
||||||
|
assert.equal(
|
||||||
|
stagioneDaPartite([{ data: "2026-03-04" }, { data: "2025-11-12" }, { data: "" }]),
|
||||||
|
"2025/26",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
raggruppaPerStagione([
|
||||||
|
{ id: "a", data: "2026-04-10" },
|
||||||
|
{ id: "b", data: "2025-10-01" },
|
||||||
|
{ id: "c", data: "2025-05-02" },
|
||||||
|
]),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
stagione: "2025/26",
|
||||||
|
match: [
|
||||||
|
{ id: "a", data: "2026-04-10" },
|
||||||
|
{ id: "b", data: "2025-10-01" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ stagione: "2024/25", match: [{ id: "c", data: "2025-05-02" }] },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
console.log("csi-core: ok");
|
console.log("csi-core: ok");
|
||||||
Reference in new issue
Block a user