Skip to content

Commit

Permalink
feat: added ability to list swiss holidays for easier camp start sele…
Browse files Browse the repository at this point in the history
…ction #12
  • Loading branch information
mario-zelger committed Nov 26, 2024
1 parent 1ea0e8c commit 222621a
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 1 deletion.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"axios": "^1.6.0",
"date-fns": "^4.1.0",
"dompurify": "^3.2.1",
"exceljs": "^4.4.0",
"i18next": "^23.8.2",
"i18next-browser-languagedetector": "^8.0.0",
Expand Down
86 changes: 86 additions & 0 deletions src/apis/openholidays-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import axios from "axios";
import { format } from "date-fns";

const client = axios.create({
baseURL: 'https://openholidaysapi.org/',
headers: {
"Content-type": "application/json",
},
});

export const loadSubdivisions = async (languageCode?: OHApiLanguageCode): Promise<OHApiSubdivision[]> => {
const params = getBaseParams(languageCode);
return (await client.get<OHApiSubdivision[]>('/subdivisions', { params: params })).data;
}

export const loadPublicHolidays = async (validFromDate: Date, validToDate: Date, languageCode?: OHApiLanguageCode): Promise<OHApiHoliday[]> => {
const params = getBaseParams(languageCode);
params.append('validFrom', format(validFromDate, 'yyyy-MM-dd'));
params.append('validTo', format(validToDate, 'yyyy-MM-dd'));

return (await client.get<OHApiHoliday[]>('/publicholidays', { params: params })).data;
}

export const loadSchoolHolidays = async (validFromDate: Date, validToDate: Date, languageCode?: OHApiLanguageCode): Promise<OHApiHoliday[]> => {
const params = getBaseParams(languageCode);
params.append('validFrom', format(validFromDate, 'yyyy-MM-dd'));
params.append('validTo', format(validToDate, 'yyyy-MM-dd'));

return (await client.get<OHApiHoliday[]>('/schoolholidays', { params: params })).data;
}

const getBaseParams = (languageCode?: OHApiLanguageCode): URLSearchParams => {
const params = new URLSearchParams();
params.append('countryIsoCode', 'CH');

if (languageCode) {
params.append('languageIsoCode', languageCode);
}

return params;
}

export const parseLanguageOrDefault = (lang: string): OHApiLanguageCode => {
const openApiLanguageCode = lang.toUpperCase();
return isValidLanguageCode(openApiLanguageCode)
? openApiLanguageCode as OHApiLanguageCode
: 'EN';
}

const isValidLanguageCode = (code: string): boolean => code === 'DE' || code === 'FR' || code === 'IT' || code === 'EN';

export type OHApiLanguageCode = 'DE' | 'FR' | 'IT' | 'EN';

export interface OHApiSubdivision {
name: OHApiLanguageText[];
shortName: string;
category: OHApiLanguageText[];
code: string;
isoCode: string;
children: string[];
officialLanguages: string[];
comment: string | null;
}

export interface OHApiHoliday {
id: string;
name: OHApiLanguageText[];
type: string;
startDate: string;
endDate: string;
nationwide: boolean;
regionalScope: string;
temporalScope: string;
subdivisions?: OHApiSubdivisionInfo[];
comment?: OHApiLanguageText[];
}

export interface OHApiLanguageText {
language: OHApiLanguageCode;
text: string;
}

export interface OHApiSubdivisionInfo {
code: string;
shortName: string;
}
14 changes: 14 additions & 0 deletions src/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"calendarPage": {
"title": "Kalender",
"startDate": "Erster Lagertag",
"viewHolidays": "Ferientage suchen",
"responsible": "Verantwortlich",
"puffer": "Puffer (Tage)",
"pufferDescription": "Reduziert das Datum der Termine um die angegebene Anzahl an Tagen.",
Expand Down Expand Up @@ -57,6 +58,19 @@
"link": "Zum Kapitel"
}
},
"holidaysModal": {
"title": "Ferien und Feiertage",
"description": "Wähle einen Kanton aus und definiere das Jahr für welches du die Ferien und Feiertage sehen möchtest. Wenn du auf ein Datum klickst wird dieses als der erste Lagertag ausgewählt.",
"canton": "Kanton",
"selectCanton": "Kanton auswählen...",
"year": "Jahr",
"from": "Von",
"to": "Bis",
"type": "Art",
"noResults": "Keine Informationen zu Ferien und Feiertage gefunden.",
"close": "Schliessen",
"dataHint": "Die Informationen werden durch das <a href=\"https://www.openholidaysapi.org/\" rel=\"noreferrer\">OpenHolidays API</a> bereitgestellt."
},
"searchPage": {
"title": "Suche",
"searchPlaceholder": "Suchbegriff eingeben...",
Expand Down
14 changes: 14 additions & 0 deletions src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"calendarPage": {
"title": "Calendrier",
"startDate": "Premier jour du camp",
"viewHolidays": "Chercher des jours de vacances",
"responsible": "Responsable",
"puffer": "Marge (jours)",
"pufferDescription": "Réduit la date des rendez-vous du nombre de jours indiqué.",
Expand Down Expand Up @@ -57,6 +58,19 @@
"link": "Au chapitre"
}
},
"holidaysModal": {
"title": "Ferien und Feiertage",
"description": "Choisis un canton et définis l'année pour laquelle tu souhaites voir les vacances et les jours fériés. Si tu cliques sur une date, celle-ci sera sélectionnée comme premier jour de camp.",
"canton": "Canton",
"selectCanton": "Sélectionner un canton...",
"year": "Année",
"from": "Du",
"to": "À",
"type": "Type",
"noResults": "Aucune information trouvée sur les vacances et les jours fériés",
"close": "Fermer",
"dataHint": "Les informations sont fournies par <a href=\"https://www.openholidaysapi.org/\" rel=\"noreferrer\">API OpenHolidays</a>."
},
"searchPage": {
"title": "Recherche",
"searchPlaceholder": "Saisir un terme de recherche...",
Expand Down
14 changes: 14 additions & 0 deletions src/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"calendarPage": {
"title": "Calendario",
"startDate": "Primo giorno del campo",
"viewHolidays": "Ricerca giorni di vacanza",
"responsible": "Responsabile",
"puffer": "Tampone (giorni)",
"pufferDescription": "Riduce la data degli appuntamenti del numero di giorni specificato.",
Expand Down Expand Up @@ -57,6 +58,19 @@
"link": "Al capitolo"
}
},
"holidaysModal": {
"title": "Ferien und Feiertage",
"description": "Selezionare un cantone e definire l'anno per il quale si desidera visualizzare le vacanze e i giorni festivi. Se si fa clic su una data, questa verrà selezionata come primo giorno del campo.",
"canton": "Canton",
"selectCanton": "Selezionare il cantone...",
"year": "Anno",
"from": "Da",
"to": "A",
"type": "Tipo",
"noResults": "Non sono state trovate informazioni sulle vacanze e sui giorni festivi.",
"close": "Chiudere",
"dataHint": "Le informazioni sono fornite da <a href=\"https://www.openholidaysapi.org/\" rel=\"noreferrer\">API di OpenHolidays</a>."
},
"searchPage": {
"title": "Cerca",
"searchPlaceholder": "Inserisci il termine di ricerca...",
Expand Down
18 changes: 17 additions & 1 deletion src/pages/calendar/components/CalendarForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ChangeEvent, useCallback, useEffect, useState } from 'react'
import React, { ChangeEvent, useContext, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next';
import CalendarTable from './CalendarTable';
import { CalendarTask } from './Task';
Expand All @@ -12,6 +12,9 @@ import { Tooltip } from 'react-tooltip'
import { sessionCache } from "../../../shared/session-cache";
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ModalContext } from "../../../components/modal/ModalContext";
import HolidaySelectModal, { HolidayModalResultData } from "./HolidaySelectModal";


const dateFormat = 'yyyy-MM-dd'
const initialStartDate = format(Date.now(), dateFormat)
Expand All @@ -26,6 +29,7 @@ const calendarDesignationCacheKey = 'calendar-designation'
function CalendarForm() {

const { t } = useTranslation()
const { openModal } = useContext(ModalContext);

const defaultCalendarDesignation = t('calendarPage.defaultDesignation');

Expand Down Expand Up @@ -87,6 +91,15 @@ function CalendarForm() {
sessionCache.set(calendarDesignationCacheKey, newPrefix);
}

const openHolidaysModal = async () => {
const result = await openModal<HolidayModalResultData>(HolidaySelectModal, {}, { isWide: true });
if (result.isCancelled || !result.data?.selectedDate) {
return
}

updateStartDate(result.data.selectedDate)
};

useEffect(() => {
const parsedStartDate = parse(startDate, dateFormat, Date.now())
const isValidDate = isValid(parsedStartDate)
Expand Down Expand Up @@ -173,6 +186,9 @@ function CalendarForm() {
</label>
<input id="startDate" type="date" name="startDate" value={startDate}
onChange={onStartDateChanged}/>
<a className="cursor-pointer" onClick={openHolidaysModal}>
{t('calendarPage.viewHolidays')}
</a>
</div>

<div className="form-entry">
Expand Down
Loading

0 comments on commit 222621a

Please sign in to comment.