import _ from 'lodash' import { render } from 'preact' import { useEffect, useState } from 'preact/hooks' import { ToolOverlay } from './components/CourseVisibility.jsx' import { EventsView, MODE_COURSE } from './components/EventsView.jsx' import { HamburgerMenu } from './components/HamburgerMenu.jsx' import { Help } from './components/Help.jsx' import { Icon } from './components/Icon.jsx' import { Popup } from './components/Popup.jsx' import { Toolbar } from './components/Toolbar.jsx' import { OptionBar } from './components/OptionBar.jsx' import { prettyAulaName, prettyProfName, usePersistentState } from './utils.jsx' window._ = _ window.dataBuffer = {} const CALENDAR_IDS = { 'anno-1': ['6308cfcb1df5cb026699ce32'], 'anno-2': ['63ce711cc3940409488f274e'], 'anno-3': ['63ce736abfadbe06acc8338d'], 'magistrale': ['63ce74397a9aee064ee01ad7'], 'tutti': [ '6308cfcb1df5cb026699ce32', '63ce711cc3940409488f274e', '63ce736abfadbe06acc8338d', '63ce74397a9aee064ee01ad7', ], } const DATE_RANGES = { '6308cfcb1df5cb026699ce32': { from: '2023-02-27T00:00:00.000Z', to: '2023-03-05T00:00:00.000Z', }, '63ce711cc3940409488f274e': { from: '2023-02-27T00:00:00.000Z', to: '2023-03-05T00:00:00.000Z', }, '63ce736abfadbe06acc8338d': { from: '2023-02-27T00:00:00.000Z', to: '2023-03-05T00:00:00.000Z', }, '63ce74397a9aee064ee01ad7': { from: '2023-02-27T00:00:00.000Z', to: '2023-03-05T00:00:00.000Z', }, } function specialEventPatches(eventi) { // Il laboratorio del primo anno in realtà è in due canali separati let i = 1 eventi.forEach(evento => { if (evento.nome === 'LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE') { evento.nome += ` (${i})` i++ } }) return eventi } async function loadEventi(ids) { const calendari = await Promise.all( ids.map(async id => { // Almost directly copy-pasted from Chrome Dev Tools const req = await fetch( 'https://apache.prod.up.cineca.it/api/Impegni/getImpegniCalendarioPubblico', { headers: { 'content-type': 'application/json;charset=UTF-8', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-site', }, body: JSON.stringify({ mostraImpegniAnnullati: true, mostraIndisponibilitaTotali: false, linkCalendarioId: id, clienteId: '628de8b9b63679f193b87046', pianificazioneTemplate: false, dataInizio: DATE_RANGES[id].from, dataFine: DATE_RANGES[id].to, }), method: 'POST', mode: 'cors', credentials: 'omit', } ) return await req.json() }) ) if (ids.length === 1) { return specialEventPatches(calendari[0]) } return specialEventPatches(_.uniqBy(_.concat(...calendari), 'id')) } const App = ({}) => { // Data Sources const [source, setSource] = usePersistentState('orario.source', 'magistrale') const [eventi, setEventi] = useState([]) // View Modes const [mode, setMode] = usePersistentState('orario.mode', MODE_COURSE) // Selection const [selectedCourses, setSelectedCourses] = usePersistentState('orario.selection', []) const [hideOtherCourses, setHideOtherCourses] = usePersistentState('orario.hide-other', false) // Menus const [helpVisible, setHelpVisible] = useState(false) const [showMobileMenu, setShowMobileMenu] = useState(false) useEffect(async () => { const eventi = await loadEventi(CALENDAR_IDS[source]) window.dataBuffer[source] = eventi setEventi(eventi) }, [source]) const groupIds = new Set(eventi.map(e => e.nome)) const toolOverlayVisible = selectedCourses.length > 0 && selectedCourses.filter(id => groupIds.has(id)).length > 0 useEffect(() => { const groupIds = new Set(eventi.map(e => e.nome)) if ( selectedCourses.length === 0 || (eventi.length > 0 && selectedCourses.filter(id => groupIds.has(id)).length === 0) ) { setHideOtherCourses(false) } }, [eventi, selectedCourses.length]) const [theme, setTheme] = usePersistentState( 'orario.theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' ) document.body.classList.toggle('dark-mode', theme === 'dark') return ( <> setShowMobileMenu(true), onHelp: () => setHelpVisible(true), theme, setTheme, }} /> setHelpVisible(true), }} /> ({ id: nome, name: _.split(nome, '-', 1)[0].trim(), start: new Date(dataInizio), end: new Date(dataFine), docenti: docenti.map(({ nome, cognome }) => prettyProfName(nome, cognome)), aule: aule.map(aula => prettyAulaName(aula.codice)), }))} /> {toolOverlayVisible && ( setHideOtherCourses(s => !s)} onClose={() => { setSelectedCourses([]) setHideOtherCourses(false) }} /> )} {showMobileMenu && ( { setShowMobileMenu(false) }, }} /> )} {helpVisible && ( Guida } onClose={() => setHelpVisible(false)} > )} ) } render(, document.body)