"use client"; import { loadSchedule } from "../lib/data"; import { useEffect, useState } from "react"; import Flag from "./flag"; import { COPY, COPY_SUCCESS, FILTER_BY_COUNTRY, FILTER_BY_SPORT, MADE_BY_FABRICE, NOT_AFFILIATED } from "../lib/text"; import useLocalStorage from "@/lib/local-storage"; import { GoogleAnalytics } from "@next/third-parties/google"; interface MultilingualString { [key: string]: string; } interface Language { code: string; name: string; } interface Sport { key: string; name: MultilingualString } interface Team { key: string; name: MultilingualString; } interface Match { team1: Team; team2: Team; } interface Event { key: string; start: string; end: string; sport: string; isTraining: boolean; medal: '0' | '1' | '3'; name: MultilingualString; match?: Match; } interface Calendar { languages: Language[]; sports: Sport[]; events: Event[]; nocs: Team[]; } const COLORS = ['azzurro', 'giallo', 'rosa', 'rosso', 'verde', 'viola']; export default function Home() { const qs = typeof window !== 'undefined' ? window.location.search ? new URLSearchParams(window.location.search) : new URLSearchParams() : new URLSearchParams(); const [data, setData] = useState(null); const [language, setLanguage] = useLocalStorage('lang', (navigator.language || 'en').split('-')[0]); const [cookieConsent, setCookieConsent] = useLocalStorage('cookie-consent', 'null'); const translate = (text: MultilingualString) => { return text[`${language}`] || text['en'] || Object.values(text)[0] || ''; }; const generateLink = ({ noc, sport, lang }: { noc?: string; sport?: string, lang?: string }) => { const currentParams = new URLSearchParams(qs.toString()); if (noc !== undefined) { if (noc === "") { currentParams.delete('noc'); } else { currentParams.set('noc', noc); } } if (sport !== undefined) { if (sport === "") { currentParams.delete('sport'); } else { currentParams.set('sport', sport); } } if (lang !== undefined) { if (lang === "") { currentParams.delete('lang'); } else { currentParams.set('lang', lang); } } const paramString = currentParams.toString(); return paramString ? `./?${paramString}` : '.'; } const generateCalendarLink = () => { const host = typeof window !== 'undefined' ? window.location.host : ''; const noc = (qs.get('noc') || 'calendar').toLowerCase(); const sport = (qs.get('sport') || 'all-sports').toLowerCase(); return `http://${host}/api/data/${language}/${sport}/${noc}.ics`; }; const getColor = (i: number) => COLORS[i % COLORS.length]; useEffect(() => { if (data == null) { loadSchedule() .then(setData) .catch(console.log); } }, [data]); const filter = (event: Event) => { let visible = true; if (event.end < new Date().toISOString()) { return false; } const sport = qs.get('sport'); if (sport && event.sport !== sport) { visible = false; } const noc = qs.get('noc'); if (noc) { if (event.match) { if (event.match.team1.key !== noc && event.match.team2.key !== noc) { visible = false; } } else { visible = false; } } return visible; } const copyToClipboard = (text: string) => { if (typeof navigator !== 'undefined' && navigator.clipboard) { const button = document.getElementById('copy_button')!; navigator.clipboard.writeText(text).then(() => { button.textContent = translate(COPY_SUCCESS); button.classList.add('text-success'); button.classList.add('font-bold'); setTimeout(() => { // document.getElementById('copy_toast')?.classList.remove('toast-open'); button.textContent = translate(COPY); button.classList.remove('text-success'); button.classList.remove('font-bold'); }, 2000); }); } } const calendarLink = generateCalendarLink(); if (data) { let lastDay = ""; if (data.languages.find(lang => lang.code === language) === undefined) { setLanguage('en') } return (
Milano Cortina 2026 Winter Olympics Calendar
  • {qs.get('sport') ? ( <>{translate(data.sports.find((sport) => sport.key === qs.get('sport'))!.name)} ) : ( <>{translate(FILTER_BY_SPORT)} )}
  • {qs.get('noc') ? ( <>{translate(data.nocs.find((noc) => noc.key === qs.get('noc'))!.name)} ) : ( <>{translate(FILTER_BY_COUNTRY)} )}
Apple Calendar Google Calendar Office 365 Calendar Outlook Calendar Yahoo Calendar
{ data.events .filter(event => filter(event)) .sort((a, b) => a.start.localeCompare(b.start)) .map((event, i) => { const startDate = new Date(event.start); const endDate = new Date(event.end); const startHours = startDate.getHours().toString().padStart(2, '0'); const startMinutes = startDate.getMinutes().toString().padStart(2, '0'); const endHours = endDate.getHours().toString().padStart(2, '0'); const endMinutes = endDate.getMinutes().toString().padStart(2, '0'); const participants = []; let titleColor = "fg-main"; if (event.medal === '1') { titleColor = "bg-gold"; } else if (event.medal === '3') { titleColor = "bg-bronze"; } if (event.match) { participants.push(event.match.team1.key); participants.push(event.match.team2.key); } const day = event.start.split('T')[0]; let dayHeader = <>; if (lastDay !== day) { dayHeader = (

{new Date(day).toLocaleDateString(language, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}

); } lastDay = day; return (
{dayHeader}
{startHours}:{startMinutes}
{endHours}:{endMinutes}
{translate(data.sports.find(sport => sport.key === event.sport)?.name || {}).toUpperCase()}
{translate(event.name)}
{event.match?.team1?.key && event.match?.team2.key && (
{translate(event.match.team1.name)}
-
{translate(event.match.team2.name)}
)}
) }) }
{cookieConsent === 'true' && } {cookieConsent === 'null' &&

This website uses cookies for statistics purposes and to enhance the user experience.

}
); } return (
Loading
); }