add non-team sports

This commit is contained in:
Fabrice Lamant
2024-07-19 10:45:02 +01:00
parent 7ae974798b
commit 5c7f237806
78 changed files with 18628 additions and 2528 deletions

37
src/ics.js Normal file
View File

@ -0,0 +1,37 @@
const fs = require("fs");
/**
* generateICS generates the calendar for given events on ICS format
* @param {string} title
* @param {string} key
* @param {object[]} events
*/
const generateICS = (title, key, events) => {
const lines = [];
lines.push("BEGIN:VCALENDAR");
lines.push("VERSION:2.0");
lines.push(`PRODID:-//fabrice404//olympics-calendar//${key}//EN`);
lines.push(`X-WR-CALNAME:${title}`);
lines.push(`NAME:${title}`);
events.forEach((event) => {
lines.push("BEGIN:VEVENT");
lines.push(
...Object.entries(event)
.filter(([key]) => !key.startsWith("_"))
.map(([key, value]) => `${key}:${value}`),
);
lines.push("END:VEVENT");
});
lines.push("END:VCALENDAR");
const calendarPath = `${__dirname}/../docs/${key}.ics`;
const folder = calendarPath.split("/").slice(0, -1).join("/");
fs.mkdirSync(folder, { recursive: true });
fs.writeFileSync(calendarPath, lines.join("\r\n"));
};
module.exports = {
generateICS,
};

225
src/index.js Normal file
View File

@ -0,0 +1,225 @@
const cheerio = require("cheerio");
const fs = require("fs");
const autoprefixer = require("autoprefixer");
const postcss = require("postcss");
const tailwindcss = require("tailwindcss");
const { getSportIcon } = require("./sports");
const { isValidNOC, getNOCName, getNOCFlag } = require("./nocs");
const { generateICS } = require("./ics");
const downloadSchedule = async (sportKey) => {
const cacheFile = `${__dirname}/../cache/${sportKey}.html`;
if (!fs.existsSync(cacheFile)) {
const response = await fetch(`https://olympics.com/en/paris-2024/schedule/${sportKey}`);
const content = await response.text();
fs.writeFileSync(cacheFile, content);
}
const html = fs.readFileSync(cacheFile, "utf-8");
const $ = cheerio.load(html);
return JSON.parse($("#__NEXT_DATA__").text());
};
const EVENTS = [];
const NOCS = [];
const SPORTS = [];
const addNOC = (noc) => {
if (!NOCS.includes(noc)) {
NOCS.push(noc);
}
};
const addSport = (sportKey, sportName) => {
if (!SPORTS.find((sport) => sport.key === sportKey)) {
SPORTS.push({ key: sportKey, name: sportName, NOCS: [] });
}
};
const addSportNOC = (sportKey, sportName, noc) => {
addSport(sportKey, sportName);
const sport = SPORTS.find((sport) => sport.key === sportKey);
if (!sport.NOCS.includes(noc)) {
sport.NOCS.push(noc);
}
};
const generateCalendars = () => {
SPORTS
.sort((a, b) => a.name > b.name ? 1 : -1)
.forEach((sport) => {
let events = EVENTS
.filter((event) => event._SPORT === sport.key)
.sort((a, b) => a.UID > b.UID ? 1 : -1);
let key = `${sport.key}/general`;
let title = `${getSportIcon(sport.key)} ${sport.name} | Paris 2024`;
generateICS(title, key, events);
sport.NOCS.forEach((noc) => {
events = EVENTS
.filter((event) => event._SPORT === sport.key && event._NOCS.includes(noc))
.sort((a, b) => a.UID > b.UID ? 1 : -1);
key = `${sport.key}/${noc}`;
title = `${getNOCFlag(noc)} ${getNOCName(noc)} ${sport.name} | Paris 2024`;
generateICS(title, key, events);
});
});
NOCS.sort()
.forEach((noc) => {
const events = EVENTS
.filter((event) => event._NOCS.includes(noc))
.sort((a, b) => a.UID > b.UID ? 1 : -1);
const key = `general/${noc}`;
const title = `${getNOCFlag(noc)} ${getNOCName(noc)} | Paris 2024`;
generateICS(title, key, events);
});
};
const extractSportCalendar = async (sportKey) => {
const data = await downloadSchedule(sportKey);
const sportName = data.query.pDisciplineLabel;
const sportIcon = getSportIcon(sportKey);
addSport(sportKey, sportName);
data.props.pageProps.scheduleDataSource.initialSchedule.units.forEach(unit => {
unit.startDateTimeUtc = new Date(unit.startDate).toISOString().replace(".000", "");
unit.endDateTimeUtc = new Date(unit.endDate).toISOString().replace(".000", "");
const event = {
UID: `${sportKey}-${unit.startDateTimeUtc.replace(/[:-]/g, "")}`,
DTSTAMP: unit.startDateTimeUtc.replace(/[:-]/g, ""),
DTSTART: unit.startDateTimeUtc.replace(/[:-]/g, ""),
DTEND: unit.endDateTimeUtc.replace(/[:-]/g, ""),
DESCRIPTION: `${sportName} - ${unit.eventUnitName}`,
SUMMARY: `${sportIcon} ${unit.eventUnitName}`.trim(),
LOCATION: unit.venueDescription,
_SPORT: sportKey,
_NOCS: [],
};
if (unit.competitors) {
const competitors = unit.competitors
.filter((competitor) => competitor.noc && isValidNOC(competitor.noc))
.sort((a, b) => a.order > b.order ? 1 : -1);
event._NOCS = competitors.map((competitor) => {
addSportNOC(sportKey, sportName, competitor.noc);
addNOC(competitor.noc);
return competitor.noc;
});
// two competitors, we put them in the summary
if (competitors.length === 2) {
const competitor1 = competitors.shift();
const competitor2 = competitors.shift();
event.UID += `-${competitor1.noc}-${competitor2.noc}`;
if (competitor1.name !== getNOCName(competitor1.noc)) {
event.SUMMARY = `${sportIcon} ${competitor1.name} ${getNOCFlag(competitor1.noc)} - ${getNOCFlag(competitor2.noc)} ${competitor2.name}`;
} else {
event.SUMMARY = `${sportIcon} ${competitor1.noc} ${getNOCFlag(competitor1.noc)} - ${getNOCFlag(competitor2.noc)} ${competitor2.noc}`;
}
} else {
// more than two, we put them in the description
competitors.forEach((competitor) => {
if (competitor.name !== getNOCName(competitor.noc)) {
event.DESCRIPTION += `\n${getNOCFlag(competitor.noc)} ${competitor.name}`;
} else {
event.DESCRIPTION += `\n${getNOCFlag(competitor.noc)} ${competitor.noc}`;
}
});
}
}
EVENTS.push(event);
});
};
const generateOutputPage = () => {
const html = [];
const linkClass = "inline-block bg-slate-400 hover:bg-blue-400 text-white px-2 py-1 my-px rounded-lg text-base";
html.push("<table>");
SPORTS.map((sport) => {
html.push(`<tr class="even:bg-slate-200">`);
html.push(`<th class="font-bold text-left">${sport.name}</td>`);
html.push(`<td>`);
html.push(`<a href="${sport.key}/general.ics" class="${linkClass}">Full schedule</a>`);
sport.NOCS.sort().forEach((noc) => {
html.push(`<a href="${sport.key}/${noc}.ics" class="${linkClass}">${getNOCFlag(noc)} ${noc}</a>`);
});
html.push("</td>");
html.push("</tr>");
});
html.push("</table>");
const template = fs.readFileSync(`${__dirname}/template.html`, "utf-8");
const output = template.replace("{{calendars}}", html.join("\n"));
fs.writeFileSync("docs/index.html", output);
postcss([autoprefixer, tailwindcss])
.process(fs.readFileSync(`${__dirname}/template.css`, "utf-8"), { from: "template.css", to: "docs/style.css" })
.then((result) => {
fs.writeFileSync("docs/style.css", result.css);
});
;
};
const main = async () => {
await Promise.all(
[
"3x3-basketball",
"archery",
"artistic-gymnastics",
"artistic-swimming",
"athletics",
"badminton",
"basketball",
"beach-volleyball",
"boxing",
"breaking",
"canoe-slalom",
"canoe-sprint",
"cycling-bmx-freestyle",
"cycling-bmx-racing",
"cycling-mountain-bike",
"cycling-road",
"cycling-track",
"diving",
"equestrian",
"fencing",
"football",
"golf",
"handball",
"hockey",
"judo",
"marathon-swimming",
"modern-pentathlon",
"rhythmic-gymnastics",
"rowing",
"rugby-sevens",
"sailing",
"shooting",
"skateboarding",
"sport-climbing",
"surfing",
"swimming",
"table-tennis",
"taekwondo",
"tennis",
"trampoline-gymnastics",
"triathlon",
"volleyball",
"water-polo",
"weightlifting",
"wrestling",
]
.map((key) => extractSportCalendar(key)),
);
generateCalendars();
generateOutputPage();
};
main();

246
src/nocs.js Normal file
View File

@ -0,0 +1,246 @@
const NOCS = {
AFG: { icon: "🇦🇫", name: "Afghanistan" },
ALB: { icon: "🇦🇱", name: "Albania" },
ALG: { icon: "🇩🇿", name: "Algeria" },
AND: { icon: "🇦🇩", name: "Andorra" },
ANG: { icon: "🇦🇴", name: "Angola" },
ANT: { icon: "🇦🇬", name: "Antigua and Barbuda" },
ARG: { icon: "🇦🇷", name: "Argentina" },
ARM: { icon: "🇦🇲", name: "Armenia" },
ARU: { icon: "🇦🇼", name: "Aruba" },
ASA: { icon: "🇦🇸", name: "American Samoa" },
AUS: { icon: "🇦🇺", name: "Australia" },
AUT: { icon: "🇦🇹", name: "Austria" },
AZE: { icon: "🇦🇿", name: "Azerbaijan" },
BAH: { icon: "🇧🇸", name: "Bahamas" },
BAN: { icon: "🇧🇩", name: "Bangladesh" },
BAR: { icon: "🇧🇧", name: "Barbados" },
BDI: { icon: "🇧🇮", name: "Burundi" },
BEL: { icon: "🇧🇪", name: "Belgium" },
BEN: { icon: "🇧🇯", name: "Benin" },
BER: { icon: "🇧🇲", name: "Bermuda" },
BHU: { icon: "🇧🇹", name: "Bhutan" },
BIH: { icon: "🇧🇦", name: "Bosnia and Herzegovina" },
BIZ: { icon: "🇧🇿", name: "Belize" },
BOL: { icon: "🇧🇴", name: "Bolivia" },
BOT: { icon: "🇧🇼", name: "Botswana" },
BRA: { icon: "🇧🇷", name: "Brazil" },
BRN: { icon: "🇧🇭", name: "Bahrain" },
BRU: { icon: "🇧🇳", name: "Brunei" },
BUL: { icon: "🇧🇬", name: "Bulgaria" },
BUR: { icon: "🇧🇫", name: "Burkina Faso" },
CAF: { icon: "🇨🇫", name: "Central African Republic" },
CAM: { icon: "🇰🇭", name: "Cambodia" },
CAN: { icon: "🇨🇦", name: "Canada" },
CAY: { icon: "🇰🇾", name: "Cayman Islands" },
CGO: { icon: "🇨🇬", name: "Congo" },
CHA: { icon: "🇹🇩", name: "Chad" },
CHI: { icon: "🇨🇱", name: "Chile" },
CHN: { icon: "🇨🇳", name: "China" },
CIV: { icon: "🇨🇮", name: "Côte d'Ivoire" },
CMR: { icon: "🇨🇲", name: "Cameroon" },
COD: { icon: "🇨🇩", name: "Democratic Republic of the Congo" },
COK: { icon: "🇨🇰", name: "Cook Islands" },
COL: { icon: "🇨🇴", name: "Colombia" },
COM: { icon: "🇰🇲", name: "Comoros" },
CPV: { icon: "🇨🇻", name: "Cabo Verde" },
CRC: { icon: "🇨🇷", name: "Costa Rica" },
CRO: { icon: "🇭🇷", name: "Croatia" },
CUB: { icon: "🇨🇺", name: "Cuba" },
CYP: { icon: "🇨🇾", name: "Cyprus" },
CZE: { icon: "🇨🇿", name: "Czechia" },
DEN: { icon: "🇩🇰", name: "Denmark" },
DJI: { icon: "🇩🇯", name: "Djibouti" },
DMA: { icon: "🇩🇲", name: "Dominica" },
DOM: { icon: "🇩🇴", name: "Dominican Republic" },
ECU: { icon: "🇪🇨", name: "Ecuador" },
EGY: { icon: "🇪🇬", name: "Egypt" },
EOR: { icon: "🏳️", name: "Refugee Olympic Team" },
ERI: { icon: "🇪🇷", name: "Eritrea" },
ESA: { icon: "🇸🇻", name: "El Salvador" },
ESP: { icon: "🇪🇸", name: "Spain" },
EST: { icon: "🇪🇪", name: "Estonia" },
ETH: { icon: "🇪🇹", name: "Ethiopia" },
FIJ: { icon: "🇫🇯", name: "Fiji" },
FIN: { icon: "🇫🇮", name: "Finland" },
FRA: { icon: "🇫🇷", name: "France" },
FSM: { icon: "🇫🇲", name: "Federated States of Micronesia" },
GAB: { icon: "🇬🇦", name: "Gabon" },
GAM: { icon: "🇬🇲", name: "Gambia" },
GBR: { icon: "🇬🇧", name: "Great Britain" },
GBS: { icon: "🇬🇼", name: "Guinea-Bissau" },
GEO: { icon: "🇬🇪", name: "Georgia" },
GEQ: { icon: "🇬🇶", name: "Equatorial Guinea" },
GER: { icon: "🇩🇪", name: "Germany" },
GHA: { icon: "🇬🇭", name: "Ghana" },
GRE: { icon: "🇬🇷", name: "Greece" },
GRN: { icon: "🇬🇩", name: "Grenada" },
GUA: { icon: "🇬🇹", name: "Guatemala" },
GUI: { icon: "🇬🇳", name: "Guinea" },
GUM: { icon: "🇬🇺", name: "Guam" },
GUY: { icon: "🇬🇾", name: "Guyana" },
HAI: { icon: "🇭🇹", name: "Haiti" },
HKG: { icon: "🇭🇰", name: "Hong Kong" },
HON: { icon: "🇭🇳", name: "Honduras" },
HUN: { icon: "🇭🇺", name: "Hungary" },
INA: { icon: "🇮🇩", name: "Indonesia" },
IND: { icon: "🇮🇳", name: "India" },
IRI: { icon: "🇮🇷", name: "Iran" },
IRL: { icon: "🇮🇪", name: "Ireland" },
IRQ: { icon: "🇮🇶", name: "Iraq" },
ISL: { icon: "🇮🇸", name: "Iceland" },
ISR: { icon: "🇮🇱", name: "Israel" },
ISV: { icon: "🇻🇮", name: "U.S. Virgin Islands" },
ITA: { icon: "🇮🇹", name: "Italy" },
IVB: { icon: "🇻🇬", name: "British Virgin Islands" },
JAM: { icon: "🇯🇲", name: "Jamaica" },
JOR: { icon: "🇯🇴", name: "Jordan" },
JPN: { icon: "🇯🇵", name: "Japan" },
KAZ: { icon: "🇰🇿", name: "Kazakhstan" },
KEN: { icon: "🇰🇪", name: "Kenya" },
KGZ: { icon: "🇰🇬", name: "Kyrgyzstan" },
KIR: { icon: "🇰🇮", name: "Kiribati" },
KOR: { icon: "🇰🇷", name: "Korea" },
KOS: { icon: "🇽🇰", name: "Kosovo" },
KSA: { icon: "🇸🇦", name: "Saudi Arabia" },
KUW: { icon: "🇰🇼", name: "Kuwait" },
LAO: { icon: "🇱🇦", name: "Laos" },
LAT: { icon: "🇱🇻", name: "Latvia" },
LBA: { icon: "🇱🇾", name: "Libya" },
LBN: { icon: "🇱🇧", name: "Lebanon" },
LBR: { icon: "🇱🇷", name: "Liberia" },
LCA: { icon: "🇱🇨", name: "Saint Lucia" },
LES: { icon: "🇱🇸", name: "Lesotho" },
LIE: { icon: "🇱🇮", name: "Liechtenstein" },
LTU: { icon: "🇱🇹", name: "Lithuania" },
LUX: { icon: "🇱🇺", name: "Luxembourg" },
MAD: { icon: "🇲🇬", name: "Madagascar" },
MAR: { icon: "🇲🇦", name: "Morocco" },
MAS: { icon: "🇲🇾", name: "Malaysia" },
MAW: { icon: "🇲🇼", name: "Malawi" },
MDA: { icon: "🇲🇩", name: "Moldova" },
MDV: { icon: "🇲🇻", name: "Maldives" },
MEX: { icon: "🇲🇽", name: "Mexico" },
MGL: { icon: "🇲🇳", name: "Mongolia" },
MHL: { icon: "🇲🇭", name: "Marshall Islands" },
MKD: { icon: "🇲🇰", name: "North Macedonia" },
MLI: { icon: "🇲🇱", name: "Mali" },
MLT: { icon: "🇲🇹", name: "Malta" },
MNE: { icon: "🇲🇪", name: "Montenegro" },
MON: { icon: "🇲🇨", name: "Monaco" },
MOZ: { icon: "🇲🇿", name: "Mozambique" },
MRI: { icon: "🇲🇺", name: "Mauritius" },
MTN: { icon: "🇲🇷", name: "Mauritania" },
MYA: { icon: "🇲🇲", name: "Myanmar" },
NAM: { icon: "🇳🇦", name: "Namibia" },
NCA: { icon: "🇳🇮", name: "Nicaragua" },
NED: { icon: "🇳🇱", name: "Netherlands" },
NEP: { icon: "🇳🇵", name: "Nepal" },
NGR: { icon: "🇳🇬", name: "Nigeria" },
NIG: { icon: "🇳🇪", name: "Niger" },
NOR: { icon: "🇳🇴", name: "Norway" },
NRU: { icon: "🇳🇷", name: "Nauru" },
NZL: { icon: "🇳🇿", name: "New Zealand" },
OMA: { icon: "🇴🇲", name: "Oman" },
PAK: { icon: "🇵🇰", name: "Pakistan" },
PAN: { icon: "🇵🇦", name: "Panama" },
PAR: { icon: "🇵🇾", name: "Paraguay" },
PER: { icon: "🇵🇪", name: "Peru" },
PHI: { icon: "🇵🇭", name: "Philippines" },
PLE: { icon: "🇵🇸", name: "Palestine" },
PLW: { icon: "🇵🇼", name: "Palau" },
PNG: { icon: "🇵🇬", name: "Papua New Guinea" },
POL: { icon: "🇵🇱", name: "Poland" },
POR: { icon: "🇵🇹", name: "Portugal" },
PRK: { icon: "🇰🇵", name: "North Korea" },
PUR: { icon: "🇵🇷", name: "Puerto Rico" },
QAT: { icon: "🇶🇦", name: "Qatar" },
ROU: { icon: "🇷🇴", name: "Romania" },
RSA: { icon: "🇿🇦", name: "South Africa" },
RWA: { icon: "🇷🇼", name: "Rwanda" },
SAM: { icon: "🇼🇸", name: "Samoa" },
SEN: { icon: "🇸🇳", name: "Senegal" },
SEY: { icon: "🇸🇨", name: "Seychelles" },
SGP: { icon: "🇸🇬", name: "Singapore" },
SKN: { icon: "🇰🇳", name: "Saint Kitts and Nevis" },
SLE: { icon: "🇸🇱", name: "Sierra Leone" },
SLO: { icon: "🇸🇮", name: "Slovenia" },
SMR: { icon: "🇸🇲", name: "San Marino" },
SOL: { icon: "🇸🇧", name: "Solomon Islands" },
SOM: { icon: "🇸🇴", name: "Somalia" },
SRB: { icon: "🇷🇸", name: "Serbia" },
SRI: { icon: "🇱🇰", name: "Sri Lanka" },
SSD: { icon: "🇸🇸", name: "South Sudan" },
STP: { icon: "🇸🇹", name: "Sao Tome and Principe" },
SUD: { icon: "🇸🇩", name: "Sudan" },
SUI: { icon: "🇨🇭", name: "Switzerland" },
SUR: { icon: "🇸🇷", name: "Suriname" },
SVK: { icon: "🇸🇰", name: "Slovakia" },
SWE: { icon: "🇸🇪", name: "Sweden" },
SWZ: { icon: "🇸🇿", name: "Eswatini" },
SYR: { icon: "🇸🇾", name: "Syria" },
TAN: { icon: "🇹🇿", name: "Tanzania" },
TGA: { icon: "🇹🇴", name: "Tonga" },
THA: { icon: "🇹🇭", name: "Thailand" },
TJK: { icon: "🇹🇯", name: "Tajikistan" },
TKM: { icon: "🇹🇲", name: "Turkmenistan" },
TLS: { icon: "🇹🇱", name: "Timor-Leste" },
TOG: { icon: "🇹🇬", name: "Togo" },
TPE: { icon: "🇹🇼", name: "Chinese Taipei" },
TTO: { icon: "🇹🇹", name: "Trinidad and Tobago" },
TUN: { icon: "🇹🇳", name: "Tunisia" },
TUR: { icon: "🇹🇷", name: "Türkiye" },
TUV: { icon: "🇹🇻", name: "Tuvalu" },
UAE: { icon: "🇦🇪", name: "United Arab Emirates" },
UGA: { icon: "🇺🇬", name: "Uganda" },
UKR: { icon: "🇺🇦", name: "Ukraine" },
URU: { icon: "🇺🇾", name: "Uruguay" },
USA: { icon: "🇺🇸", name: "United States" },
UZB: { icon: "🇺🇿", name: "Uzbekistan" },
VAN: { icon: "🇻🇺", name: "Vanuatu" },
VEN: { icon: "🇻🇪", name: "Venezuela" },
VIE: { icon: "🇻🇳", name: "Vietnam" },
VIN: { icon: "🇻🇨", name: "Saint Vincent and the Grenadines" },
YEM: { icon: "🇾🇪", name: "Yemen" },
ZAM: { icon: "🇿🇲", name: "Zambia" },
ZIM: { icon: "🇿🇼", name: "Zimbabwe" },
};
/**
* isValidNOC checks if the NOC code is in the NOCS list
* @param {string} noc National Olympic Committee code
* @returns {boolean}
*/
const isValidNOC = (noc) => NOCS[noc] !== undefined;
/**
* getNOC returns the NOC name and icon from the NOC code
* @param {string} noc National Olympic Committee code
* @returns {object}
*/
const getNOC = (noc) => {
if (isValidNOC(noc)) {
return NOCS[noc];
}
throw new Error(`NOC code ${noc} not found`);
};
/**
* getNOCFlag returns the NOC icon from the NOC code
* @param {string} noc National Olympic Committee code
* @returns {string}
*/
const getNOCFlag = (noc) => getNOC(noc).icon;
/**
* getNOCName returns the NOC name from the NOC code
* @param {string} noc National Olympic Committee code
* @returns
*/
const getNOCName = (noc) => getNOC(noc).name;
module.exports = {
isValidNOC,
getNOCFlag,
getNOCName,
};

59
src/sports.js Normal file
View File

@ -0,0 +1,59 @@
const SPORTS = {
"3x3-basketball": "🏀",
"archery": "🏹",
"artistic-gymnastics": "🤸",
"artistic-swimming": "🏊",
"athletics": "🏃",
"badminton": "🏸",
"basketball": "🏀",
"beach-volleyball": "🏐",
"boxing": "🥊",
"breaking": "🤸",
"canoe-slalom": "🛶",
"canoe-sprint": "🛶",
"cycling-bmx-freestyle": "🚴",
"cycling-bmx-racing": "🚴",
"cycling-mountain-bike": "🚴",
"cycling-road": "🚴",
"cycling-track": "🚴",
"diving": "🏊",
"equestrian": "🏇",
"fencing": "🤺",
"football": "⚽",
"golf": "⛳",
"handball": "🤾",
"hockey": "🏑",
"judo": "🥋",
"marathon-swimming": "🏊",
"modern-pentathlon": "🤺",
"rhythmic-gymnastics": "🤸",
"rowing": "🚣",
"rugby-sevens": "🏉",
"sailing": "⛵",
"shooting": "🔫",
"skateboarding": "🛹",
"sport-climbing": "🧗",
"surfing": "🏄",
"swimming": "🏊",
"table-tennis": "🏓",
"taekwondo": "🥋",
"tennis":"🎾",
"trampoline-gymnastics": "🤸",
"triathlon": "🏊",
"volleyball": "🏐",
"water-polo": "🤽",
"weightlifting": "🏋",
"wrestling": "🤼",
};
const getSportIcon = (sport) => {
if (SPORTS[sport]) {
return SPORTS[sport];
}
console.error(`No icon set for ${sport}`);
return "";
};
module.exports = {
getSportIcon,
};

7
src/template.css Normal file
View File

@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
font-size: 12px;
}

57
src/template.html Normal file
View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<title>Paris 2024 Summer Olympic Games calendars</title>
<link href="./style.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Paris 2024 Summer Olympic Games calendars">
<meta name="keywords" content="Paris 2024, Summer Olympic Games, calendars">
<meta name="author" content="Fabrice LAMANT">
</head>
<body>
<div class="p-4">
<div class="flex flex-wrap items-center justify-between border-b pb-4 border-slate-900/10">
<h1 class="text-4xl">Paris 2024 Summer Olympic Games calendars</h1>
<div>
<a href="https://github.com/fabrice404/olympics-calendar" target="_blank"
class="ml-6 block text-slate-200 hover:text-sky-500 w-10 h-10">
<svg viewBox="0 0 16 16" class="w-10 h-10" fill="currentColor" aria-hidden="true">
<path
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z">
</path>
</svg></a>
</div>
</div>
<div>
<h2 class="text-3xl pb-4 pt-8">How to add a calendar to your Google calendar</h2>
<ol class="list-decimal ml-10">
<li>Right click on a calendar and select "Copy link address"</li>
<li>Open your Google calendar</li>
<li>Click the plus sign next to “Other Calendars” on the left menu</li>
<li>Click "From URL"</li>
<li>Paste your calendar URL</li>
<li>Click "Add calendar"</li>
</ol>
</div>
<div>
{{calendars}}
</div>
<div class="text-sm my-10 text-center">
This webiste is not affiliated with the International Olympic Committee.
All trademarks, logos and brand names are the property of their respective owners.
</div>
</div>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0KQC1F1K4H"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-0KQC1F1K4H');
</script>
</body>

0
src/template.js Normal file
View File