Compare commits
22
Commits
feat/schema
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c6f60e461 | ||
|
|
e6c5db05a6 | ||
|
|
d0c2f58094 | ||
|
|
e9539b7f35 | ||
|
|
4c98950822 | ||
|
|
d929aa6cfe | ||
|
|
4d95a1a633 | ||
|
|
935309147c | ||
|
|
fb551fb719 | ||
|
|
c731d9c8b9 | ||
|
|
d9d2aaed11 | ||
|
|
7bbd9ba139 | ||
|
|
62a9b80b21 | ||
|
|
c43bdc60e7 | ||
|
|
c8172ad88a | ||
|
|
c0b2894c22 | ||
|
|
fd0440465c | ||
|
|
bdbf031bce | ||
|
|
32dc9a6104 | ||
|
|
2d4a3ce664 | ||
|
|
af41177561 | ||
|
|
22ce830c80 |
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 90,
|
||||||
|
"singleQuote": true,
|
||||||
|
"quoteProps": "consistent",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": false,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"proseWrap": "always"
|
||||||
|
}
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
[
|
|
||||||
"ALGEBRA 1",
|
|
||||||
"ANALISI ARMONICA - ANALISI ARMONICA/a",
|
|
||||||
"ANALISI MATEMATICA 1",
|
|
||||||
"ANALISI MATEMATICA 2",
|
|
||||||
"ANALISI MATEMATICA 3",
|
|
||||||
"ANALISI NUMERICA CON LABORATORIO - ANALISI NUMERICA",
|
|
||||||
"ARITMETICA",
|
|
||||||
"ASPETTI MATEMATICI NELLA COMPUTAZIONE QUANTISTICA",
|
|
||||||
"CALCOLO SCIENTIFICO",
|
|
||||||
"COMBINATORIA ALGEBRICA",
|
|
||||||
"DETERMINAZIONE ORBITALE",
|
|
||||||
"DINAMICA DEL SISTEMA SOLARE",
|
|
||||||
"ELEMENTI DI GEOMETRIA ALGEBRICA",
|
|
||||||
"ELEMENTI DI TOPOLOGIA ALGEBRICA",
|
|
||||||
"FISICA II",
|
|
||||||
"FONDAMENTI DI PROGRAMMAZIONE CON LABORATORIO - FONDAMENTI DI PROGRAMMAZIONE ",
|
|
||||||
"GEOMETRIA 1",
|
|
||||||
"GEOMETRIA 2 - GEOMETRIA 2 A",
|
|
||||||
"GEOMETRIA E TOPOLOGIA DIFFERENZIALE",
|
|
||||||
"INGLESE SCIENTIFICO",
|
|
||||||
"ISTITUZIONI DI ALGEBRA",
|
|
||||||
"ISTITUZIONI DI ANALISI MATEMATICA",
|
|
||||||
"ISTITUZIONI DI DIDATTICA DELLA MATEMATICA",
|
|
||||||
"ISTITUZIONI DI FISICA MATEMATICA",
|
|
||||||
"LABORATORIO COMPUTAZIONALE",
|
|
||||||
"LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE",
|
|
||||||
"LOGICA MATEMATICA",
|
|
||||||
"MECCANICA SUPERIORE - MECCANICA SUPERIORE/a",
|
|
||||||
"METODI NUMERICI PER CATENE DI MARKOV - METODI NUMERICI PER CATENE DI MARKOV/a",
|
|
||||||
"METODI NUMERICI PER LA GRAFICA - METODI NUMERICI PER LA GRAFICA/a",
|
|
||||||
"PROBABILITÀ",
|
|
||||||
"RICERCA OPERATIVA",
|
|
||||||
"SISTEMI DINAMICI",
|
|
||||||
"STORIA DELLA MATEMATICA",
|
|
||||||
"TECNOLOGIE PER LA DIDATTICA",
|
|
||||||
"TEORIA ANALITICA DEI NUMERI A - TEORIA ANALITICA DEI NUMERI A/a",
|
|
||||||
"TEORIA E METODI DELL'OTTIMIZZAZIONE",
|
|
||||||
"TOPOLOGIA DIFFERENZIALE - TOPOLOGIA DIFFERENZIALE/a"
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { useRef } from 'preact/hooks'
|
||||||
|
import { Icon } from './Icon.jsx'
|
||||||
|
|
||||||
|
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||||
|
|
||||||
|
export const DatePicker = ({ date, setDate }) => {
|
||||||
|
const input = useRef()
|
||||||
|
|
||||||
|
const [year, month, day] = date.split('T')[0].split('-')
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="date-picker"
|
||||||
|
onClick={() =>
|
||||||
|
isSafari ? input.current.focus() : input.current.showPicker()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
ref={input}
|
||||||
|
type="date"
|
||||||
|
value={`${year}-${month}-${day}`}
|
||||||
|
onChange={e => setDate(new Date(e.target.value).toISOString())}
|
||||||
|
/>
|
||||||
|
<div class="date-picker-render">
|
||||||
|
<div class="date">
|
||||||
|
{day}/{month}/{year}
|
||||||
|
</div>
|
||||||
|
<div class="calendar">
|
||||||
|
<Icon name="calendar_month" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ const viewModeMap = {
|
|||||||
export const EventsView = ({ mode, source, ...viewProps }) => {
|
export const EventsView = ({ mode, source, ...viewProps }) => {
|
||||||
// const Mode = viewModeMap[mode]
|
// const Mode = viewModeMap[mode]
|
||||||
|
|
||||||
|
|
||||||
if (source === 'orario') {
|
if (source === 'orario') {
|
||||||
return (
|
return (
|
||||||
<div class="events-view">
|
<div class="events-view">
|
||||||
|
|||||||
@@ -1,27 +1,17 @@
|
|||||||
import { ComboBox } from './ComboBox.jsx'
|
import { ComboBox } from './ComboBox.jsx'
|
||||||
import { MODE_COURSES, MODE_SCHEDULE, MODE_WORKWEEK, MODE_WORKWEEK_GRID } from './EventsView.jsx'
|
import {
|
||||||
|
MODE_COURSES,
|
||||||
|
MODE_SCHEDULE,
|
||||||
|
MODE_WORKWEEK,
|
||||||
|
MODE_WORKWEEK_GRID,
|
||||||
|
} from './EventsView.jsx'
|
||||||
|
import { DatePicker } from './DatePicker.jsx'
|
||||||
import { Help } from './Help.jsx'
|
import { Help } from './Help.jsx'
|
||||||
import { Icon } from './Icon.jsx'
|
import { Icon } from './Icon.jsx'
|
||||||
|
|
||||||
export const HamburgerMenu = ({ onClose, theme, setTheme }) => {
|
export const HamburgerMenu = ({ date, setDate, onClose, theme, setTheme }) => {
|
||||||
return (
|
return (
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="header">
|
|
||||||
<div class="option-group">
|
|
||||||
<button class="flat icon" onClick={onClose}>
|
|
||||||
<Icon name="close" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
|
|
||||||
>
|
|
||||||
<Icon name={theme === 'dark' ? 'dark_mode' : 'light_mode'} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="item logo">
|
|
||||||
<img src="logo-circuit-board.svg" alt="logo" /> / <span>Orario</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="help">
|
<div class="help">
|
||||||
<h2>
|
<h2>
|
||||||
<Icon name="info" />
|
<Icon name="info" />
|
||||||
|
|||||||
+17
-13
@@ -4,27 +4,31 @@ export const Help = ({}) => (
|
|||||||
<>
|
<>
|
||||||
<h3>Visualizzazione Corsi</h3>
|
<h3>Visualizzazione Corsi</h3>
|
||||||
<p>
|
<p>
|
||||||
Per visualizzare i corsi che ti interessano nella tabella
|
Per visualizzare i corsi che ti interessano nella tabella dell'orario, devi
|
||||||
dell'orario, devi prima selezionarli. Puoi selezionare dei corsi
|
prima selezionarli. Puoi selezionare dei corsi cliccandoli, cercandoli nelle
|
||||||
cliccandoli, cercandoli nelle sezioni Primo anno (<b>I</b>), Secondo
|
sezioni Primo anno (<b>I</b>), Secondo anno (<b>II</b>), Terzo anno (
|
||||||
anno (<b>II</b>), Terzo anno (<b>III</b>), Magistrale (<b>M</b>), o
|
<b>III</b>), Magistrale (<b>M</b>), o Tutti.
|
||||||
Tutti.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Una volta compiuta la selezione, è possibile vedere la tabella delle
|
Una volta compiuta la selezione, è possibile vedere la tabella delle lezioni
|
||||||
lezioni andando nella visualizzazione Orario
|
andando nella visualizzazione Orario (
|
||||||
|
<Icon name="calendar_view_month" />)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Per via di eventuali preferenze personali, è possibile cambiare
|
Per via di eventuali preferenze personali, è possibile cambiare l'orientazione
|
||||||
l'orientazione della tabella Orario trasponendola, utilizzando il
|
della tabella Orario trasponendola, utilizzando il pulsante Trasponi (
|
||||||
pulsante Trasponi (
|
|
||||||
<Icon name="switch_left" style="transform: rotate(-45deg)" />)
|
<Icon name="switch_left" style="transform: rotate(-45deg)" />)
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
È anche possibile visualizzare in uno specchietto riassuntivo soltanto i corsi
|
||||||
|
selezionati, andando nella visualizzazione Lista (
|
||||||
|
<Icon name="list" />)
|
||||||
|
</p>
|
||||||
<h3>Stampa</h3>
|
<h3>Stampa</h3>
|
||||||
<p>
|
<p>
|
||||||
Da desktop puoi stampare l'orario attualmente visibile con il
|
Da desktop puoi stampare l'orario attualmente visibile con il bottone{' '}
|
||||||
bottone <Icon name="print" /> (è consigliato controllare le opzioni
|
<Icon name="print" /> (è consigliato controllare le opzioni di stampa per
|
||||||
di stampa per ottenere un risultato soddisfacente).
|
ottenere un risultato soddisfacente).
|
||||||
</p>
|
</p>
|
||||||
<h3>Bug & Contatti</h3>
|
<h3>Bug & Contatti</h3>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CompoundButton } from './CompoundButton.jsx'
|
import { CompoundButton } from './CompoundButton.jsx'
|
||||||
import { Icon } from './Icon.jsx'
|
import { Icon } from './Icon.jsx'
|
||||||
|
|
||||||
export const OptionBar = ({ source, setSource }) => {
|
export const OptionBar = ({ view, setView }) => {
|
||||||
return (
|
return (
|
||||||
<div class="option-bar">
|
<div class="option-bar">
|
||||||
<div class="option-group">
|
<div class="option-group">
|
||||||
@@ -14,20 +14,25 @@ export const OptionBar = ({ source, setSource }) => {
|
|||||||
{ value: 'magistrale', label: 'M' },
|
{ value: 'magistrale', label: 'M' },
|
||||||
{ value: 'tutti', label: 'Tutti' },
|
{ value: 'tutti', label: 'Tutti' },
|
||||||
]}
|
]}
|
||||||
value={source}
|
value={view}
|
||||||
setValue={setSource}
|
setValue={setView}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CompoundButton
|
<CompoundButton
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
value: 'orario',
|
value: 'orario',
|
||||||
label: <Icon name="calendar_month" />,
|
label: <Icon name="calendar_view_month" />,
|
||||||
|
icon: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'lista',
|
||||||
|
label: <Icon name="list" />,
|
||||||
icon: true,
|
icon: true,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
value={source}
|
value={view}
|
||||||
setValue={setSource}
|
setValue={setView}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { DatePicker } from './DatePicker.jsx'
|
||||||
|
import { Icon } from './Icon.jsx'
|
||||||
|
|
||||||
|
export const SettingsBar = ({ theme, setTheme, date, setDate }) => {
|
||||||
|
return (
|
||||||
|
<div class="settings-bar">
|
||||||
|
<div class="settings-group">
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
|
||||||
|
>
|
||||||
|
<Icon name={theme === 'dark' ? 'dark_mode' : 'light_mode'} />
|
||||||
|
</button>
|
||||||
|
<DatePicker date={date} setDate={setDate} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
+17
-11
@@ -1,10 +1,14 @@
|
|||||||
import { CompoundButton } from './CompoundButton.jsx'
|
import { CompoundButton } from './CompoundButton.jsx'
|
||||||
|
import { DatePicker } from './DatePicker.jsx'
|
||||||
import { Icon } from './Icon.jsx'
|
import { Icon } from './Icon.jsx'
|
||||||
|
|
||||||
export const Toolbar = ({
|
export const Toolbar = ({
|
||||||
source,
|
source,
|
||||||
setSource,
|
setSource,
|
||||||
onShowMenu,
|
date,
|
||||||
|
setDate,
|
||||||
|
showMobileMenu,
|
||||||
|
setShowMobileMenu,
|
||||||
onHelp,
|
onHelp,
|
||||||
theme,
|
theme,
|
||||||
setTheme,
|
setTheme,
|
||||||
@@ -12,13 +16,15 @@ export const Toolbar = ({
|
|||||||
return (
|
return (
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div class="mobile">
|
<div class="mobile">
|
||||||
<button class="flat icon" onClick={onShowMenu}>
|
<button
|
||||||
<Icon name="menu" />
|
class="flat icon"
|
||||||
|
onClick={() => setShowMobileMenu(!showMobileMenu)}
|
||||||
|
>
|
||||||
|
<Icon name={`${showMobileMenu ? 'close' : 'menu'}`} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="item logo">
|
<div class="item logo">
|
||||||
<img src="logo-circuit-board.svg" alt="logo" /> /{' '}
|
<img src="logo-circuit-board.svg" alt="logo" /> / <span>Orario</span>
|
||||||
<span>Orario</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="option-group">
|
<div class="option-group">
|
||||||
<div class="item option">
|
<div class="item option">
|
||||||
@@ -38,12 +44,16 @@ export const Toolbar = ({
|
|||||||
<CompoundButton
|
<CompoundButton
|
||||||
options={[
|
options={[
|
||||||
{ value: 'orario', label: 'Orario' },
|
{ value: 'orario', label: 'Orario' },
|
||||||
|
{ value: 'lista', label: 'Lista' },
|
||||||
]}
|
]}
|
||||||
value={source}
|
value={source}
|
||||||
setValue={setSource}
|
setValue={setSource}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="option-group">
|
||||||
|
<DatePicker date={date} setDate={setDate} />
|
||||||
|
</div>
|
||||||
<div class="option-group">
|
<div class="option-group">
|
||||||
<div class="item option">
|
<div class="item option">
|
||||||
<button class="icon" onClick={() => window.print()}>
|
<button class="icon" onClick={() => window.print()}>
|
||||||
@@ -53,13 +63,9 @@ export const Toolbar = ({
|
|||||||
<div class="item option">
|
<div class="item option">
|
||||||
<button
|
<button
|
||||||
class="icon"
|
class="icon"
|
||||||
onClick={() =>
|
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
|
||||||
setTheme(theme === 'dark' ? 'light' : 'dark')
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon name={theme === 'dark' ? 'dark_mode' : 'light_mode'} />
|
||||||
name={theme === 'dark' ? 'dark_mode' : 'light_mode'}
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="item option">
|
<div class="item option">
|
||||||
|
|||||||
@@ -3,12 +3,23 @@ import { format } from 'date-fns'
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||||
import { prettyCourseName, WEEK_DAYS } from '../../utils.jsx'
|
import { prettyCourseName, WEEK_DAYS } from '../../utils.jsx'
|
||||||
|
import { Icon } from '../Icon.jsx'
|
||||||
|
|
||||||
export const Courses = ({ source, timetables, selection, setSelection }) => {
|
export const Courses = ({
|
||||||
|
source,
|
||||||
|
timetables,
|
||||||
|
selection,
|
||||||
|
setSelection,
|
||||||
|
hideOtherCourses,
|
||||||
|
}) => {
|
||||||
const events = timetables[source]
|
const events = timetables[source]
|
||||||
const selectionSet = new Set(selection)
|
const selectionSet = new Set(selection)
|
||||||
|
|
||||||
const eventsByCourse = _.groupBy(_.sortBy(events, 'id'), 'id')
|
const visibleEvents = hideOtherCourses
|
||||||
|
? events.filter(e => selectionSet.has(e.id))
|
||||||
|
: events
|
||||||
|
|
||||||
|
const eventsByCourse = _.groupBy(_.sortBy(visibleEvents, 'id'), 'id')
|
||||||
|
|
||||||
const profsPerCourse = _.mapValues(eventsByCourse, events =>
|
const profsPerCourse = _.mapValues(eventsByCourse, events =>
|
||||||
_.uniq(events.flatMap(event => event.docenti))
|
_.uniq(events.flatMap(event => event.docenti))
|
||||||
@@ -42,6 +53,15 @@ export const Courses = ({ source, timetables, selection, setSelection }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="course-view" ref={element}>
|
<div class="course-view" ref={element}>
|
||||||
|
{hideOtherCourses && selection.length === 0 && (
|
||||||
|
<div class="warning">
|
||||||
|
<p>Non hai ancora selezionato nessun corso.</p>
|
||||||
|
<p>
|
||||||
|
Clicca sui corsi nelle altre visuali per selezionarli e
|
||||||
|
visualizzarli nella lista
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div class="wrap-container">
|
<div class="wrap-container">
|
||||||
{Object.entries(eventsByCourse).map(([id, courseEvents]) => (
|
{Object.entries(eventsByCourse).map(([id, courseEvents]) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import { useEffect, useRef, useState } from 'preact/hooks'
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { differenceInMinutes, startOfDay } from 'date-fns'
|
import { differenceInMinutes, startOfDay } from 'date-fns'
|
||||||
|
|
||||||
import {
|
import { WEEK_DAYS_SHORT, prettyCourseName, usePersistentState } from '../../utils.jsx'
|
||||||
WEEK_DAYS_SHORT,
|
|
||||||
prettyCourseName,
|
|
||||||
usePersistentState,
|
|
||||||
} from '../../utils.jsx'
|
|
||||||
import { layoutEvents, layoutIntervals } from '../../interval-layout.js'
|
import { layoutEvents, layoutIntervals } from '../../interval-layout.js'
|
||||||
import { Popup } from '../Popup.jsx'
|
import { Popup } from '../Popup.jsx'
|
||||||
import { Icon } from '../Icon.jsx'
|
import { Icon } from '../Icon.jsx'
|
||||||
@@ -17,28 +13,23 @@ const TransposePopup = ({ onClose }) => {
|
|||||||
<Popup
|
<Popup
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
<Icon name="info" /> Attenzione! La tabella è stata
|
<Icon name="info" /> Attenzione! La tabella è stata trasposta!
|
||||||
trasposta!
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
|
<p>A grande richiesta popolare abbiamo trasposto la tabella dell'orario!</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
A grande richiesta popolare abbiamo trasposto la tabella
|
Assicurati quindi di leggerla correttamente (dall'alto verso il basso
|
||||||
dell'orario!
|
invece che da sinistra verso destra).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Assicurati quindi di leggerla correttamente (dall'alto verso il
|
Se preferisci utilizzare la versione vecchia, puoi utilizzare il pulsante
|
||||||
basso invece che da sinistra verso destra).
|
Trasponi <Icon name="switch_left" style="transform: rotate(-45deg)" />{' '}
|
||||||
</p>
|
nell'origine della tabella per trasporla. Questa scelta verrà salvata nei
|
||||||
|
cookie e verrà ricordata in futuro
|
||||||
<p>
|
|
||||||
Se preferisci utilizzare la versione vecchia, puoi utilizzare il
|
|
||||||
pulsante Trasponi{' '}
|
|
||||||
<Icon name="switch_left" style="transform: rotate(-45deg)" />{' '}
|
|
||||||
nell'origine della tabella per trasporla. Questa scelta verrà
|
|
||||||
salvata nei cookie e verrà ricordata in futuro
|
|
||||||
</p>
|
</p>
|
||||||
</Popup>
|
</Popup>
|
||||||
)
|
)
|
||||||
@@ -46,18 +37,17 @@ const TransposePopup = ({ onClose }) => {
|
|||||||
|
|
||||||
const NoCourseWarning = () => {
|
const NoCourseWarning = () => {
|
||||||
return (
|
return (
|
||||||
<div class="no-courses-warning">
|
<div class="warning">
|
||||||
<p>Non hai ancora selezionato nessun corso.</p>
|
<p>Non hai ancora selezionato nessun corso.</p>
|
||||||
<p>
|
<p>
|
||||||
Clicca sui corsi nelle altre visuali per selezionarli e
|
Clicca sui corsi nelle altre visuali per selezionarli e visualizzarli
|
||||||
visualizzarli nell'orario
|
nell'orario
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = ({ layout, day, colors }) => {
|
const Layout = ({ layout, day, colors }) => {
|
||||||
console.log(layout)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{layout.map(block => (
|
{layout.map(block => (
|
||||||
@@ -76,13 +66,10 @@ const Layout = ({ layout, day, colors }) => {
|
|||||||
style={{
|
style={{
|
||||||
'--block-size': block.end - block.start,
|
'--block-size': block.end - block.start,
|
||||||
'--size': event.end - event.start,
|
'--size': event.end - event.start,
|
||||||
'--relative-start':
|
'--relative-start': event.start - block.start,
|
||||||
event.start - block.start,
|
|
||||||
'--index': event.index,
|
'--index': event.index,
|
||||||
'--of': block.layers,
|
'--of': block.layers,
|
||||||
'--color': `var(--event-${
|
'--color': `var(--event-${colors[event.id]})`,
|
||||||
colors[event.id]
|
|
||||||
})`,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="event">
|
<div class="event">
|
||||||
@@ -119,16 +106,11 @@ const ScheduleGrid = ({
|
|||||||
class="small"
|
class="small"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setOrientation(
|
setOrientation(
|
||||||
orientation === 'original'
|
orientation === 'original' ? 'transposed' : 'original'
|
||||||
? 'transposed'
|
|
||||||
: 'original'
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon name="switch_left" style="transform: rotate(-45deg)" />
|
||||||
name="switch_left"
|
|
||||||
style="transform: rotate(-45deg)"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{[1, 2, 3, 4, 5].map(n => (
|
{[1, 2, 3, 4, 5].map(n => (
|
||||||
@@ -211,25 +193,14 @@ const ScheduleCard = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Schedule = ({ timetables, selection, setSelection }) => {
|
export const Schedule = ({ timetables, selection }) => {
|
||||||
const [hasSeenTranspose, setHasSeenTranspose] = usePersistentState(
|
const [hasSeenTranspose, setHasSeenTranspose] = usePersistentState(
|
||||||
'transpose_info',
|
'transpose_info',
|
||||||
'false'
|
'false'
|
||||||
)
|
)
|
||||||
const [orientation, setOrientation] = usePersistentState(
|
const [orientation, setOrientation] = usePersistentState('orientation', 'original')
|
||||||
'orientation',
|
|
||||||
'original'
|
|
||||||
)
|
|
||||||
|
|
||||||
const colorList = [
|
const colorList = ['red', 'purple', 'blue', 'yellow', 'green', 'orange', 'lightblue']
|
||||||
'red',
|
|
||||||
'purple',
|
|
||||||
'blue',
|
|
||||||
'yellow',
|
|
||||||
'green',
|
|
||||||
'orange',
|
|
||||||
'lightblue',
|
|
||||||
]
|
|
||||||
|
|
||||||
const allEvents = timetables['tutti']
|
const allEvents = timetables['tutti']
|
||||||
const selectionSet = new Set(selection)
|
const selectionSet = new Set(selection)
|
||||||
|
|||||||
+3
-10
@@ -42,10 +42,7 @@ function layoutBlockEvents(events) {
|
|||||||
let viableIndex = 0
|
let viableIndex = 0
|
||||||
while (
|
while (
|
||||||
result.filter(
|
result.filter(
|
||||||
e =>
|
e => e.index === viableIndex && e.start < event.end && event.start < e.end
|
||||||
e.index === viableIndex &&
|
|
||||||
e.start < event.end &&
|
|
||||||
event.start < e.end
|
|
||||||
).length !== 0
|
).length !== 0
|
||||||
) {
|
) {
|
||||||
viableIndex += 1
|
viableIndex += 1
|
||||||
@@ -57,8 +54,7 @@ function layoutBlockEvents(events) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
export function layoutEvents(events) {
|
export function layoutEvents(events) {
|
||||||
const overlap = (event, block) =>
|
const overlap = (event, block) => event.start < block.end && block.start < event.end
|
||||||
event.start < block.end && block.start < event.end
|
|
||||||
|
|
||||||
events.sort((a, b) => a.start - b.start)
|
events.sort((a, b) => a.start - b.start)
|
||||||
|
|
||||||
@@ -68,10 +64,7 @@ export function layoutEvents(events) {
|
|||||||
if (blocks.length > 0) {
|
if (blocks.length > 0) {
|
||||||
layout = layout.filter(block => !overlap(event, block))
|
layout = layout.filter(block => !overlap(event, block))
|
||||||
layout.push({
|
layout.push({
|
||||||
start: Math.min(
|
start: Math.min(event.start, ...blocks.map(block => block.start)),
|
||||||
event.start,
|
|
||||||
...blocks.map(block => block.start)
|
|
||||||
),
|
|
||||||
end: Math.max(event.end, ...blocks.map(block => block.end)),
|
end: Math.max(event.end, ...blocks.map(block => block.end)),
|
||||||
events: blocks.flatMap(block => block.events).concat([event]),
|
events: blocks.flatMap(block => block.events).concat([event]),
|
||||||
})
|
})
|
||||||
|
|||||||
+135
-79
@@ -25,39 +25,44 @@ import {
|
|||||||
usePersistentState,
|
usePersistentState,
|
||||||
} from './utils.jsx'
|
} from './utils.jsx'
|
||||||
|
|
||||||
|
import { SettingsBar } from './components/SettingsBar.jsx'
|
||||||
|
|
||||||
// Che fanno queste due righe?
|
// Che fanno queste due righe?
|
||||||
window._ = _
|
window._ = _
|
||||||
window.dataBuffer = {}
|
window.dataBuffer = {}
|
||||||
|
|
||||||
|
// NOTA: magistrale *non* è quello con i corsi a cavallo
|
||||||
const TIMETABLE_IDS = {
|
const TIMETABLE_IDS = {
|
||||||
'anno-1': '64a7c1c651f079001d52e9c8',
|
'anno-1': '687e66d62d13e6001edd366d',
|
||||||
'anno-2': '6308e2dc09352a0208fefdd9',
|
'anno-2': '687e681301ee9300198320e6',
|
||||||
'anno-3': '6308e42a1df5cb026699ced4',
|
'anno-3': '687e68bb01ee9300198320eb',
|
||||||
'magistrale': '64a7c7091ab813002c5d9ede',
|
'magistrale': '687e6a9f96ea080019d439d5',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_DATE_RANGE = {
|
// const DEFAULT_DATE_RANGE = {
|
||||||
from: '2023-10-09T00:00:00.000Z',
|
// from: '2023-10-09T00:00:00.000Z',
|
||||||
to: '2023-10-14T00:00:00.000Z',
|
// to: '2023-10-14T00:00:00.000Z',
|
||||||
}
|
// }
|
||||||
|
|
||||||
const DATE_RANGES = {
|
// const DATE_RANGES = {
|
||||||
'64a7c1c651f079001d52e9c8': DEFAULT_DATE_RANGE,
|
// '64a7c1c651f079001d52e9c8': DEFAULT_DATE_RANGE,
|
||||||
'6308e2dc09352a0208fefdd9': DEFAULT_DATE_RANGE,
|
// '6308e2dc09352a0208fefdd9': DEFAULT_DATE_RANGE,
|
||||||
'6308e42a1df5cb026699ced4': DEFAULT_DATE_RANGE,
|
// '6308e42a1df5cb026699ced4': DEFAULT_DATE_RANGE,
|
||||||
'64a7c7091ab813002c5d9ede': DEFAULT_DATE_RANGE,
|
// '64a7c7091ab813002c5d9ede': DEFAULT_DATE_RANGE,
|
||||||
}
|
// }
|
||||||
|
|
||||||
function specialEventPatches(eventi) {
|
function specialEventPatches(eventi) {
|
||||||
// Il laboratorio del primo anno in realtà è in due canali separati
|
// Il laboratorio del primo anno in realtà è in due gruppi separati
|
||||||
let i = 1
|
|
||||||
eventi.forEach(evento => {
|
eventi.forEach(evento => {
|
||||||
if (
|
if (
|
||||||
evento.nome ===
|
evento.nome === 'LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE'
|
||||||
'LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE'
|
|
||||||
) {
|
) {
|
||||||
evento.nome += ` (${i})`
|
if (evento.evento.dettagliDidattici[0].partizione.descrizione === 'CORSO A') {
|
||||||
i++
|
evento.nome += ' (A)'
|
||||||
|
}
|
||||||
|
if (evento.evento.dettagliDidattici[0].partizione.descrizione === 'CORSO B') {
|
||||||
|
evento.nome += ' (B)'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -71,15 +76,24 @@ function formatEvents(timetable) {
|
|||||||
name: _.split(nome, '-', 1)[0].trim(),
|
name: _.split(nome, '-', 1)[0].trim(),
|
||||||
start: new Date(dataInizio),
|
start: new Date(dataInizio),
|
||||||
end: new Date(dataFine),
|
end: new Date(dataFine),
|
||||||
docenti: docenti.map(({ nome, cognome }) =>
|
docenti: docenti.map(({ nome, cognome }) => prettyProfName(nome, cognome)),
|
||||||
prettyProfName(nome, cognome)
|
|
||||||
),
|
|
||||||
aule: aule.map(aula => prettyAulaName(aula.codice)),
|
aule: aule.map(aula => prettyAulaName(aula.codice)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadCalendari() {
|
async function loadCalendari(date) {
|
||||||
|
function getMonday(d) {
|
||||||
|
const day = d.getDay()
|
||||||
|
const diff = d.getDate() - day + (day === 0 ? -6 : 1)
|
||||||
|
const monday = new Date(d.setDate(diff))
|
||||||
|
monday.setUTCHours(0, 0, 0, 0)
|
||||||
|
return monday
|
||||||
|
}
|
||||||
|
const monday = getMonday(date)
|
||||||
|
const saturday = new Date(monday)
|
||||||
|
saturday.setDate(monday.getDate() + 5)
|
||||||
|
|
||||||
async function req(id) {
|
async function req(id) {
|
||||||
// Almost directly copy-pasted from Chrome Dev Tools
|
// Almost directly copy-pasted from Chrome Dev Tools
|
||||||
const req = await fetch(
|
const req = await fetch(
|
||||||
@@ -97,8 +111,8 @@ async function loadCalendari() {
|
|||||||
linkCalendarioId: id,
|
linkCalendarioId: id,
|
||||||
clienteId: '628de8b9b63679f193b87046',
|
clienteId: '628de8b9b63679f193b87046',
|
||||||
pianificazioneTemplate: false,
|
pianificazioneTemplate: false,
|
||||||
dataInizio: DATE_RANGES[id].from,
|
dataInizio: monday.toISOString(),
|
||||||
dataFine: DATE_RANGES[id].to,
|
dataFine: saturday.toISOString(),
|
||||||
}),
|
}),
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
@@ -119,7 +133,7 @@ async function loadCalendari() {
|
|||||||
const timetablesRaw = results.map(timetable =>
|
const timetablesRaw = results.map(timetable =>
|
||||||
specialEventPatches(_.uniqBy(timetable, 'id'))
|
specialEventPatches(_.uniqBy(timetable, 'id'))
|
||||||
)
|
)
|
||||||
const allRaw = specialEventPatches(_.uniqBy(_.concat(...results), 'id'))
|
const allRaw = specialEventPatches(_.concat(...results), 'id')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'anno-1': formatEvents(timetablesRaw[0]),
|
'anno-1': formatEvents(timetablesRaw[0]),
|
||||||
@@ -130,37 +144,58 @@ async function loadCalendari() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const View = ({ view, selection, setSelection, timetables }) => {
|
||||||
|
if (view === 'orario') {
|
||||||
|
return <Schedule selection={selection} timetables={timetables} />
|
||||||
|
} else if (view === 'lista') {
|
||||||
|
return (
|
||||||
|
<Courses
|
||||||
|
selection={selection}
|
||||||
|
setSelection={setSelection}
|
||||||
|
source={'tutti'}
|
||||||
|
timetables={timetables}
|
||||||
|
hideOtherCourses={true}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Courses
|
||||||
|
selection={selection}
|
||||||
|
setSelection={setSelection}
|
||||||
|
source={view}
|
||||||
|
timetables={timetables}
|
||||||
|
hideOtherCourses={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
const App = ({}) => {
|
const App = ({}) => {
|
||||||
// Clear persistent states unless state_token corresponds to the one passed
|
// Clear persistent states unless state_token corresponds to the one passed
|
||||||
// as the argument. Useful with breaking updates. Change this token if your
|
// as the argument. Useful with breaking updates. Change this token if your
|
||||||
// (breaking) update needs a reset of persistent states to avoid crashes.
|
// (breaking) update needs a reset of persistent states to avoid crashes.
|
||||||
//
|
//
|
||||||
// Use any random string of your choice
|
// Use any random string of your choice
|
||||||
clearOldPersistentStates('e73cba02')
|
// clearOldPersistentStates('e73cba02')
|
||||||
|
|
||||||
|
const [date, setDate] = useState(new Date().toISOString())
|
||||||
|
|
||||||
// Data Sources
|
// Data Sources
|
||||||
const [source, setSource] = usePersistentState('source', 'magistrale')
|
const [view, setView] = usePersistentState('view', 'tutti')
|
||||||
const [timetables, setTimetables] = useState(null)
|
const [timetables, setTimetables] = useState(null)
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
setTimetables(await loadCalendari())
|
setTimetables(await loadCalendari(new Date(date)))
|
||||||
}, [])
|
}, [date])
|
||||||
|
|
||||||
// View Modes
|
// View Modes
|
||||||
// const [mode, setMode] = usePersistentState('orario.mode', MODE_COURSES)
|
// const [mode, setMode] = usePersistentState('orario.mode', MODE_COURSES)
|
||||||
|
|
||||||
// Selection
|
// Selection
|
||||||
const [selectedCourses, setSelectedCourses] = usePersistentState(
|
const [selectedCourses, setSelectedCourses] = usePersistentState('selection', [])
|
||||||
'selection',
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
const [helpVisible, setHelpVisible] = useState(false)
|
const [helpVisible, setHelpVisible] = useState(false)
|
||||||
const [showMobileMenu, setShowMobileMenu] = useState(false)
|
const [showMobileMenu, setShowMobileMenu] = useState(false)
|
||||||
|
|
||||||
// const groupIds = new Set(eventi.map(e => e.nome))
|
|
||||||
const toolOverlayVisible = selectedCourses.length > 0
|
|
||||||
|
|
||||||
const [theme, setTheme] = usePersistentState(
|
const [theme, setTheme] = usePersistentState(
|
||||||
'theme',
|
'theme',
|
||||||
'light'
|
'light'
|
||||||
@@ -175,61 +210,44 @@ const App = ({}) => {
|
|||||||
<>
|
<>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
{...{
|
{...{
|
||||||
source,
|
source: view,
|
||||||
setSource,
|
setSource: setView,
|
||||||
onShowMenu: () => setShowMobileMenu(true),
|
date: date,
|
||||||
|
setDate: setDate,
|
||||||
|
showMobileMenu: showMobileMenu,
|
||||||
|
setShowMobileMenu: setShowMobileMenu,
|
||||||
onHelp: () => setHelpVisible(true),
|
onHelp: () => setHelpVisible(true),
|
||||||
theme,
|
theme,
|
||||||
setTheme,
|
setTheme,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{showMobileMenu ? (
|
||||||
|
<SettingsBar
|
||||||
|
{...{
|
||||||
|
theme,
|
||||||
|
setTheme,
|
||||||
|
date,
|
||||||
|
setDate,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<OptionBar
|
<OptionBar
|
||||||
{...{
|
{...{
|
||||||
source,
|
view: view,
|
||||||
setSource,
|
setView: setView,
|
||||||
onHelp: () => setHelpVisible(true),
|
onHelp: () => setHelpVisible(true),
|
||||||
}}
|
}}
|
||||||
orizzontale
|
orizzontale
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{timetables && (
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{source === 'orario' ? (
|
{timetables &&
|
||||||
<Schedule
|
(showMobileMenu ? (
|
||||||
selection={selectedCourses}
|
|
||||||
setSelection={setSelectedCourses}
|
|
||||||
start={new Date(2022, 10, 3)}
|
|
||||||
source={source}
|
|
||||||
timetables={timetables}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Courses
|
|
||||||
selection={selectedCourses}
|
|
||||||
setSelection={setSelectedCourses}
|
|
||||||
start={new Date(2022, 10, 3)}
|
|
||||||
source={source}
|
|
||||||
timetables={timetables}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* toolOverlayVisible && (
|
|
||||||
<ToolOverlay
|
|
||||||
mode={mode}
|
|
||||||
toggleMode={() =>
|
|
||||||
setMode(
|
|
||||||
mode === MODE_COURSES ? MODE_SCHEDULE : MODE_COURSES
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClose={() => {
|
|
||||||
setSelectedCourses([])
|
|
||||||
setMode(MODE_COURSES)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) */}
|
|
||||||
{showMobileMenu && (
|
|
||||||
<HamburgerMenu
|
<HamburgerMenu
|
||||||
{...{
|
{...{
|
||||||
|
date,
|
||||||
|
setDate,
|
||||||
theme,
|
theme,
|
||||||
setTheme,
|
setTheme,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
@@ -237,7 +255,45 @@ const App = ({}) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
) : timetables['tutti'].length === 0 ? (
|
||||||
|
<div class="warning">
|
||||||
|
<p>
|
||||||
|
Non esistono corsi per la settimana selezionata: buone
|
||||||
|
vacanze! 🎉
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Clicca sul bottone qua sotto per vedere la prima settimana
|
||||||
|
completa di lezioni:
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setDate('2025-09-29T00:00:00.000Z')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Vai al 29 settembre! 🚀
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* <p>
|
||||||
|
Per cambiare settimana puoi usare il widget Calendario (
|
||||||
|
<Icon name="calendar_month" />) in alto a destra
|
||||||
|
<br />
|
||||||
|
In versione mobile, il widget Calendario è situato dentro
|
||||||
|
il Menu (
|
||||||
|
<Icon name="menu" />)
|
||||||
|
</p> */}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<View
|
||||||
|
selection={selectedCourses}
|
||||||
|
setSelection={setSelectedCourses}
|
||||||
|
view={view}
|
||||||
|
timetables={timetables}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* showMobileMenu && (
|
||||||
|
) */}
|
||||||
{helpVisible && (
|
{helpVisible && (
|
||||||
<Popup
|
<Popup
|
||||||
title={
|
title={
|
||||||
|
|||||||
+130
-85
@@ -374,6 +374,47 @@ button,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.date-picker {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 2.5rem;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
|
||||||
|
background: var(--bg-500);
|
||||||
|
border: 2px solid var(--border-400);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
input {
|
||||||
|
z-index: -1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-render {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
.material-symbols-outlined {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Extension Classes
|
// Extension Classes
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
@@ -444,7 +485,8 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-bar {
|
.option-bar,
|
||||||
|
.settings-bar {
|
||||||
@extend .panel;
|
@extend .panel;
|
||||||
|
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
@@ -469,11 +511,21 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-group {
|
.option-group,
|
||||||
|
.settings-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-bar {
|
||||||
|
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,17 +534,37 @@ body {
|
|||||||
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
||||||
height: calc(100vh - 8rem);
|
height: calc(100vh - 8rem);
|
||||||
}
|
}
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
padding: 1rem 0rem;
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
@extend .text-block;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.course-view {
|
.course-view {
|
||||||
padding: 1rem;
|
padding: 0rem 1rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
height: 100%;
|
min-height: 100%;
|
||||||
|
|
||||||
overflow-y: scroll;
|
text-align: center;
|
||||||
|
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
.wrap-container {
|
.wrap-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -550,30 +622,18 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.schedule-view {
|
.schedule-view {
|
||||||
height: 100%;
|
min-height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 57rem;
|
max-width: 57rem;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
padding: 1rem 0.5rem;
|
padding: 0rem 0.5rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
.no-courses-warning {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
p {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.schedule-card {
|
.schedule-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@@ -584,8 +644,7 @@ body {
|
|||||||
border: 1px solid var(--border-600);
|
border: 1px solid var(--border-600);
|
||||||
border-radius: 10px 10px 0 0;
|
border-radius: 10px 10px 0 0;
|
||||||
|
|
||||||
@media screen and (max-width: $device-s-width),
|
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
||||||
(pointer: coarse) {
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,10 +699,7 @@ body {
|
|||||||
|
|
||||||
&.original {
|
&.original {
|
||||||
grid-template-columns: auto repeat(5, 1fr);
|
grid-template-columns: auto repeat(5, 1fr);
|
||||||
grid-template-rows: min-content repeat(
|
grid-template-rows: min-content repeat(var(--time-slots), 1fr);
|
||||||
var(--time-slots),
|
|
||||||
1fr
|
|
||||||
);
|
|
||||||
|
|
||||||
.transpose-button,
|
.transpose-button,
|
||||||
.day-label {
|
.day-label {
|
||||||
@@ -670,22 +726,16 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.event-block-wrapper {
|
.event-block-wrapper {
|
||||||
grid-row: calc(var(--time-start) + 2) /
|
grid-row: calc(var(--time-start) + 2) / calc(var(--time-end) + 2);
|
||||||
calc(var(--time-end) + 2);
|
|
||||||
grid-column: calc(var(--day-position) + 1);
|
grid-column: calc(var(--day-position) + 1);
|
||||||
|
|
||||||
.event-block {
|
.event-block {
|
||||||
.event-wrapper {
|
.event-wrapper {
|
||||||
width: calc(100% / var(--of));
|
width: calc(100% / var(--of));
|
||||||
height: calc(
|
height: calc(100% * var(--size) / var(--block-size));
|
||||||
100% * var(--size) / var(--block-size)
|
|
||||||
);
|
|
||||||
transform: translateX(calc(100% * var(--index)))
|
transform: translateX(calc(100% * var(--index)))
|
||||||
translateY(
|
translateY(
|
||||||
calc(
|
calc(100% * var(--relative-start) / var(--size))
|
||||||
100% * var(--relative-start) /
|
|
||||||
var(--size)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -693,10 +743,7 @@ body {
|
|||||||
}
|
}
|
||||||
&.transposed {
|
&.transposed {
|
||||||
grid-template-rows: auto repeat(5, 1fr);
|
grid-template-rows: auto repeat(5, 1fr);
|
||||||
grid-template-columns: min-content repeat(
|
grid-template-columns: min-content repeat(var(--time-slots), 1fr);
|
||||||
var(--time-slots),
|
|
||||||
1fr
|
|
||||||
);
|
|
||||||
|
|
||||||
.transpose-button,
|
.transpose-button,
|
||||||
.day-label {
|
.day-label {
|
||||||
@@ -732,15 +779,10 @@ body {
|
|||||||
.event-block {
|
.event-block {
|
||||||
.event-wrapper {
|
.event-wrapper {
|
||||||
height: calc(100% / var(--of));
|
height: calc(100% / var(--of));
|
||||||
width: calc(
|
width: calc(100% * var(--size) / var(--block-size));
|
||||||
100% * var(--size) / var(--block-size)
|
|
||||||
);
|
|
||||||
transform: translateY(calc(100% * var(--index)))
|
transform: translateY(calc(100% * var(--index)))
|
||||||
translateX(
|
translateX(
|
||||||
calc(
|
calc(100% * var(--relative-start) / var(--size))
|
||||||
100% * var(--relative-start) /
|
|
||||||
var(--size)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -773,47 +815,12 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
display: flex;
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
bottom: 3rem;
|
|
||||||
right: 3rem;
|
|
||||||
|
|
||||||
// on mobile
|
|
||||||
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
|
||||||
bottom: 1rem;
|
|
||||||
right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
|
|
||||||
border-radius: 100%;
|
|
||||||
|
|
||||||
.material-symbols-outlined {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
color: var(--accent-900);
|
|
||||||
|
|
||||||
box-shadow: 0 0.25rem 0.75rem #00000033;
|
|
||||||
}
|
|
||||||
|
|
||||||
animation: fade-in 150ms ease-in forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
left: 0;
|
// left: 0;
|
||||||
width: 100vw;
|
width: 100%;
|
||||||
height: 100vh;
|
min-height: 100%;
|
||||||
|
|
||||||
background: var(--bg-500);
|
background: var(--bg-500);
|
||||||
|
|
||||||
@@ -866,8 +873,46 @@ body {
|
|||||||
@extend .text-block;
|
@extend .text-block;
|
||||||
|
|
||||||
padding: 1rem 1rem 2.5rem;
|
padding: 1rem 1rem 2.5rem;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
|
||||||
|
bottom: 3rem;
|
||||||
|
right: 3rem;
|
||||||
|
|
||||||
|
// on mobile
|
||||||
|
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
||||||
|
bottom: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
.material-symbols-outlined {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: var(--accent-900);
|
||||||
|
|
||||||
|
box-shadow: 0 0.25rem 0.75rem #00000033;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation: fade-in 150ms ease-in forwards;
|
||||||
|
}
|
||||||
|
|
||||||
// not on mobile
|
// not on mobile
|
||||||
|
|
||||||
|
|||||||
+5
-18
@@ -12,15 +12,7 @@ export const WEEK_DAYS = [
|
|||||||
'Sabato',
|
'Sabato',
|
||||||
]
|
]
|
||||||
|
|
||||||
export const WEEK_DAYS_SHORT = [
|
export const WEEK_DAYS_SHORT = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab']
|
||||||
'Dom',
|
|
||||||
'Lun',
|
|
||||||
'Mar',
|
|
||||||
'Mer',
|
|
||||||
'Gio',
|
|
||||||
'Ven',
|
|
||||||
'Sab'
|
|
||||||
]
|
|
||||||
|
|
||||||
// Hashing
|
// Hashing
|
||||||
|
|
||||||
@@ -32,12 +24,8 @@ export function hashString(str, seed = 0) {
|
|||||||
h1 = Math.imul(h1 ^ ch, 2654435761)
|
h1 = Math.imul(h1 ^ ch, 2654435761)
|
||||||
h2 = Math.imul(h2 ^ ch, 1597334677)
|
h2 = Math.imul(h2 ^ ch, 1597334677)
|
||||||
}
|
}
|
||||||
h1 =
|
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909)
|
||||||
Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
|
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909)
|
||||||
Math.imul(h2 ^ (h2 >>> 13), 3266489909)
|
|
||||||
h2 =
|
|
||||||
Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
|
|
||||||
Math.imul(h1 ^ (h1 >>> 13), 3266489909)
|
|
||||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0)
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +38,7 @@ export function prettyCourseName(name) {
|
|||||||
.map(word => {
|
.map(word => {
|
||||||
if (word.trim().length === 0) return word
|
if (word.trim().length === 0) return word
|
||||||
|
|
||||||
return /(^del|^nel|^di$|^dei$|^con$|^alla$|^per$|^e$|^la$)/.test(
|
return /(^del|^nel|^di$|^dei$|^con$|^alla$|^per$|^e$|^la$)/.test(word)
|
||||||
word
|
|
||||||
)
|
|
||||||
? word
|
? word
|
||||||
: word[0].toUpperCase() + word.slice(1)
|
: word[0].toUpperCase() + word.slice(1)
|
||||||
})
|
})
|
||||||
@@ -61,6 +47,7 @@ export function prettyCourseName(name) {
|
|||||||
.replaceAll('IIi', 'III')
|
.replaceAll('IIi', 'III')
|
||||||
.replaceAll('Iii', 'III')
|
.replaceAll('Iii', 'III')
|
||||||
.replaceAll(/'(.)/g, ({}, letter) => "'" + letter.toUpperCase())
|
.replaceAll(/'(.)/g, ({}, letter) => "'" + letter.toUpperCase())
|
||||||
|
.replaceAll(/\((a|b)\)/g, ({}, letter) => '(' + letter.toUpperCase() + ')')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prettyAulaName(name) {
|
export function prettyAulaName(name) {
|
||||||
|
|||||||
Reference in New Issue
Block a user