Author SHA1 Message Date
aziis98 0c6f60e461 fix: wrong default 2025-08-27 15:04:57 +02:00
aziis98 e6c5db05a6 feat: first time button sends to all courses tab 2025-08-27 13:06:12 +02:00
BachoSeven d0c2f58094 chore: update calendar IDs for 1st semester 25/26 2025-08-15 20:33:12 +02:00
BachoSeven e9539b7f35 Fix lab1 split courses recognition, add snapping! 2025-02-14 16:39:35 +01:00
aziis98 4c98950822 chore: updated to new semester calendar ids 2025-02-04 10:57:14 +01:00
aziis98 d929aa6cfe chore: updated calendar ids to first semester of 2024/2025 2024-08-06 19:00:20 +02:00
aziis98 4d95a1a633 chore: empty commit to trigger deploy 2024-02-25 17:16:44 +01:00
aziis98 935309147c cmd: prettier -w src/ 2024-02-25 16:43:59 +01:00
aziis98 fb551fb719 fix: safari ios showPicker() bug 2024-02-25 16:42:10 +01:00
BachoSeven c731d9c8b9 Merge pull request 'chore: fix LIMCO courses' names' (#13) from dev into main
continuous-integration/drone/push Build is passing
Reviewed-on: #13
2024-02-04 15:35:53 +00:00
BachoSeven d9d2aaed11 chore: fix LIMCO courses' names 2024-02-04 16:35:20 +01:00
BachoSeven 7bbd9ba139 Merge pull request 'chore: update calendar IDs for the 2nd semester' (#12) from dev into main
continuous-integration/drone/push Build is passing
Reviewed-on: #12
2024-02-04 15:00:00 +00:00
BachoSeven 62a9b80b21 chore: update calendar IDs for the 2nd semester 2024-02-04 15:59:07 +01:00
Fran314 c43bdc60e7 Merge pull request 'dev' (#11) from dev into main
continuous-integration/drone/push Build is passing
Reviewed-on: #11
2023-10-09 13:30:58 +00:00
aziis98 bdbf031bce Merge pull request 'fix: rimosso un console.log' (#8) from dev into main
continuous-integration/drone/push Build is passing
Reviewed-on: #8
2023-10-08 11:12:04 +00:00
Fran314 2d4a3ce664 Merge pull request 'reintrodotta visualizzazione lista rimossa nel commit precedente' (#7) from dev into main
continuous-integration/drone/push Build is passing
Reviewed-on: #7
2023-10-07 21:05:33 +00:00
Fran314 22ce830c80 Merge pull request 'dev' (#6) from dev into main
continuous-integration/drone/push Build is passing
Reviewed-on: #6
2023-10-07 18:56:33 +00:00
16 changed files with 154 additions and 389 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"printWidth": 90,
"singleQuote": true,
"quoteProps": "consistent",
"tabWidth": 4,
"semi": false,
"arrowParens": "avoid",
"proseWrap": "always"
}
Executable
BIN
View File
Binary file not shown.
-40
View File
@@ -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"
]
+1 -2
View File
@@ -16,8 +16,7 @@
"lodash-es": "^4.17.21",
"preact": "^10.10.6",
"sass": "^1.54.8",
"vite": "^3.0.9",
"yaml": "^2.3.2"
"vite": "^3.0.9"
},
"devDependencies": {
"@babel/core": "^7.18.13",
-8
View File
@@ -23,9 +23,6 @@ dependencies:
vite:
specifier: ^3.0.9
version: 3.0.9(sass@1.54.8)
yaml:
specifier: ^2.3.2
version: 2.3.2
devDependencies:
'@babel/core':
@@ -940,8 +937,3 @@ packages:
sass: 1.54.8
optionalDependencies:
fsevents: 2.3.2
/yaml@2.3.2:
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
engines: {node: '>= 14'}
dev: false
+8 -1
View File
@@ -1,12 +1,19 @@
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={() => input.current.showPicker()}>
<div
class="date-picker"
onClick={() =>
isSafari ? input.current.focus() : input.current.showPicker()
}
>
<input
ref={input}
type="date"
-1
View File
@@ -24,7 +24,6 @@ const viewModeMap = {
export const EventsView = ({ mode, source, ...viewProps }) => {
// const Mode = viewModeMap[mode]
if (source === 'orario') {
return (
+13 -15
View File
@@ -4,33 +4,31 @@ export const Help = ({}) => (
<>
<h3>Visualizzazione Corsi</h3>
<p>
Per visualizzare i corsi che ti interessano nella tabella
dell'orario, devi prima selezionarli. Puoi selezionare dei corsi
cliccandoli, cercandoli nelle sezioni Primo anno (<b>I</b>), Secondo
anno (<b>II</b>), Terzo anno (<b>III</b>), Magistrale (<b>M</b>), o
Tutti.
Per visualizzare i corsi che ti interessano nella tabella dell'orario, devi
prima selezionarli. Puoi selezionare dei corsi cliccandoli, cercandoli nelle
sezioni Primo anno (<b>I</b>), Secondo anno (<b>II</b>), Terzo anno (
<b>III</b>), Magistrale (<b>M</b>), o Tutti.
</p>
<p>
Una volta compiuta la selezione, è possibile vedere la tabella delle
lezioni andando nella visualizzazione Orario (
Una volta compiuta la selezione, è possibile vedere la tabella delle lezioni
andando nella visualizzazione Orario (
<Icon name="calendar_view_month" />)
</p>
<p>
Per via di eventuali preferenze personali, è possibile cambiare
l'orientazione della tabella Orario trasponendola, utilizzando il
pulsante Trasponi (
Per via di eventuali preferenze personali, è possibile cambiare l'orientazione
della tabella Orario trasponendola, utilizzando il pulsante Trasponi (
<Icon name="switch_left" style="transform: rotate(-45deg)" />)
</p>
<p>
È anche possibile visualizzare in uno specchietto riassuntivo
soltanto i corsi selezionati, andando nella visualizzazione Lista (
È anche possibile visualizzare in uno specchietto riassuntivo soltanto i corsi
selezionati, andando nella visualizzazione Lista (
<Icon name="list" />)
</p>
<h3>Stampa</h3>
<p>
Da desktop puoi stampare l'orario attualmente visibile con il
bottone <Icon name="print" /> (è consigliato controllare le opzioni
di stampa per ottenere un risultato soddisfacente).
Da desktop puoi stampare l'orario attualmente visibile con il bottone{' '}
<Icon name="print" /> (è consigliato controllare le opzioni di stampa per
ottenere un risultato soddisfacente).
</p>
<h3>Bug &amp; Contatti</h3>
<p>
+2 -16
View File
@@ -7,23 +7,9 @@ export const SettingsBar = ({ theme, setTheme, date, setDate }) => {
<div class="settings-group">
<button
class="icon"
onClick={() =>
setTheme(theme === 'dark' ? 'light' : 'dark')
}
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
<Icon
name={theme === 'dark' ? 'dark_mode' : 'light_mode'}
/>
</button>
<button
class="icon"
onClick={() =>
setTheme(theme === 'dark' ? 'light' : 'dark')
}
>
<Icon
name="add"
/>
<Icon name={theme === 'dark' ? 'dark_mode' : 'light_mode'} />
</button>
<DatePicker date={date} setDate={setDate} />
</div>
+6 -13
View File
@@ -24,8 +24,7 @@ export const Toolbar = ({
</button>
</div>
<div class="item logo">
<img src="logo-circuit-board.svg" alt="logo" /> /{' '}
<span>Orario</span>
<img src="logo-circuit-board.svg" alt="logo" /> / <span>Orario</span>
</div>
<div class="option-group">
<div class="item option">
@@ -36,7 +35,6 @@ export const Toolbar = ({
{ value: 'anno-3', label: 'III' },
{ value: 'magistrale', label: 'Magistrale' },
{ value: 'tutti', label: 'Tutti' },
{ value: 'custom', label: 'Custom' },
]}
value={source}
setValue={setSource}
@@ -52,10 +50,9 @@ export const Toolbar = ({
setValue={setSource}
/>
</div>
<div class="empty"></div>
<div class="item option">
<DatePicker date={date} setDate={setDate} />
</div>
</div>
<div class="option-group">
<DatePicker date={date} setDate={setDate} />
</div>
<div class="option-group">
<div class="item option">
@@ -66,13 +63,9 @@ export const Toolbar = ({
<div class="item option">
<button
class="icon"
onClick={() =>
setTheme(theme === 'dark' ? 'light' : 'dark')
}
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
<Icon
name={theme === 'dark' ? 'dark_mode' : 'light_mode'}
/>
<Icon name={theme === 'dark' ? 'dark_mode' : 'light_mode'} />
</button>
</div>
<div class="item option">
+15 -36
View File
@@ -2,7 +2,7 @@ import { format } from 'date-fns'
import _ from 'lodash'
import { useEffect, useRef, useState } from 'preact/hooks'
import { parseCustomEvents, prettyCourseName, WEEK_DAYS } from '../../utils.jsx'
import { prettyCourseName, WEEK_DAYS } from '../../utils.jsx'
import { Icon } from '../Icon.jsx'
export const Courses = ({
@@ -10,18 +10,13 @@ export const Courses = ({
timetables,
selection,
setSelection,
custom,
isRestrictedList,
hideOtherCourses,
}) => {
const events = isRestrictedList ? timetables['tutti'] : timetables[source]
const events = timetables[source]
const selectionSet = new Set(selection)
console.log(events)
const visibleEvents = isRestrictedList
? events
.filter(e => selectionSet.has(e.id))
.concat(parseCustomEvents(custom))
const visibleEvents = hideOtherCourses
? events.filter(e => selectionSet.has(e.id))
: events
const eventsByCourse = _.groupBy(_.sortBy(visibleEvents, 'id'), 'id')
@@ -58,7 +53,7 @@ export const Courses = ({
return (
<div class="course-view" ref={element}>
{isRestrictedList && selection.length === 0 && (
{hideOtherCourses && selection.length === 0 && (
<div class="warning">
<p>Non hai ancora selezionato nessun corso.</p>
<p>
@@ -72,39 +67,23 @@ export const Courses = ({
<div
class={
'course' +
(currentlyHovered === id && !isRestrictedList
? ' highlight'
: '') +
(selectionSet.has(id) && !isRestrictedList
? ' selected'
: '')
(currentlyHovered === id ? ' highlight' : '') +
(selectionSet.has(id) ? ' selected' : '')
}
data-course-id={id}
onClick={() => {
if (isRestrictedList) return
if (!selectionSet.has(id))
setSelection([...selection, id])
else
setSelection(
selection.filter(selId => selId !== id)
)
if (!selectionSet.has(id)) setSelection([...selection, id])
else setSelection(selection.filter(selId => selId !== id))
}}
>
<div class="title">
{prettyCourseName(courseEvents[0].name)}
</div>
<div class="docenti">
{profsPerCourse[id].join(', ')}
</div>
<div class="title">{prettyCourseName(courseEvents[0].name)}</div>
<div class="docenti">{profsPerCourse[id].join(', ')}</div>
<div class="events">
{courseEvents.map(course => (
<div>
{WEEK_DAYS[course.day]} {course.start / 60}:
{String(course.start % 60).padStart(2, '0')}
&ndash;
{course.end / 60}:
{String(course.end % 60).padStart(2, '0')}{' '}
{course.aule.join(', ')}
{WEEK_DAYS[course.start.getDay()]}{' '}
{format(course.start, 'H:mm')}&ndash;
{format(course.end, 'H:mm')} {course.aule.join(', ')}
</div>
))}
</div>
+28 -52
View File
@@ -1,15 +1,9 @@
import { useEffect, useRef, useState } from 'preact/hooks'
import _, { parseInt } from 'lodash'
import _ from 'lodash'
import { differenceInMinutes, startOfDay } from 'date-fns'
import { parse } from 'yaml'
import { parseCustomEvents } from '../../utils.jsx'
import {
WEEK_DAYS_SHORT,
prettyCourseName,
usePersistentState,
} from '../../utils.jsx'
import { WEEK_DAYS_SHORT, prettyCourseName, usePersistentState } from '../../utils.jsx'
import { layoutEvents, layoutIntervals } from '../../interval-layout.js'
import { Popup } from '../Popup.jsx'
import { Icon } from '../Icon.jsx'
@@ -19,28 +13,23 @@ const TransposePopup = ({ onClose }) => {
<Popup
title={
<>
<Icon name="info" /> Attenzione! La tabella è stata
trasposta!
<Icon name="info" /> Attenzione! La tabella è stata trasposta!
</>
}
onClose={onClose}
>
<p>A grande richiesta popolare abbiamo trasposto la tabella dell'orario!</p>
<p>
A grande richiesta popolare abbiamo trasposto la tabella
dell'orario!
Assicurati quindi di leggerla correttamente (dall'alto verso il basso
invece che da sinistra verso destra).
</p>
<p>
Assicurati quindi di leggerla correttamente (dall'alto verso il
basso invece che da sinistra verso destra).
</p>
<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
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>
</Popup>
)
@@ -51,8 +40,8 @@ const NoCourseWarning = () => {
<div class="warning">
<p>Non hai ancora selezionato nessun corso.</p>
<p>
Clicca sui corsi nelle altre visuali per selezionarli e
visualizzarli nell'orario
Clicca sui corsi nelle altre visuali per selezionarli e visualizzarli
nell'orario
</p>
</div>
)
@@ -77,13 +66,10 @@ const Layout = ({ layout, day, colors }) => {
style={{
'--block-size': block.end - block.start,
'--size': event.end - event.start,
'--relative-start':
event.start - block.start,
'--relative-start': event.start - block.start,
'--index': event.index,
'--of': block.layers,
'--color': `var(--event-${
colors[event.id]
})`,
'--color': `var(--event-${colors[event.id]})`,
}}
>
<div class="event">
@@ -120,16 +106,11 @@ const ScheduleGrid = ({
class="small"
onClick={() =>
setOrientation(
orientation === 'original'
? 'transposed'
: 'original'
orientation === 'original' ? 'transposed' : 'original'
)
}
>
<Icon
name="switch_left"
style="transform: rotate(-45deg)"
/>
<Icon name="switch_left" style="transform: rotate(-45deg)" />
</button>
</div>
{[1, 2, 3, 4, 5].map(n => (
@@ -212,30 +193,25 @@ const ScheduleCard = ({
)
}
export const Schedule = ({ timetables, selection, custom }) => {
export const Schedule = ({ timetables, selection }) => {
const [hasSeenTranspose, setHasSeenTranspose] = usePersistentState(
'transpose_info',
'false'
)
const [orientation, setOrientation] = usePersistentState(
'orientation',
'original'
)
const [orientation, setOrientation] = usePersistentState('orientation', 'original')
const colorList = [
'red',
'purple',
'blue',
'yellow',
'green',
'orange',
'lightblue',
]
const colorList = ['red', 'purple', 'blue', 'yellow', 'green', 'orange', 'lightblue']
const allEvents = timetables['tutti']
const selectionSet = new Set(selection)
const events = allEvents.filter(e => selectionSet.has(e.id))
parseCustomEvents(custom).forEach(event => events.push(event))
const events = allEvents
.filter(e => selectionSet.has(e.id))
.map(e => ({
...e,
day: e.start.getDay(),
start: differenceInMinutes(e.start, startOfDay(e.start)),
end: differenceInMinutes(e.end, startOfDay(e.start)),
}))
const weekStart = Math.min(...events.map(e => e.start), 9 * 60) / 30
const weekEnd = Math.max(...events.map(e => e.end), 18 * 60) / 30
+3 -10
View File
@@ -42,10 +42,7 @@ function layoutBlockEvents(events) {
let viableIndex = 0
while (
result.filter(
e =>
e.index === viableIndex &&
e.start < event.end &&
event.start < e.end
e => e.index === viableIndex && e.start < event.end && event.start < e.end
).length !== 0
) {
viableIndex += 1
@@ -57,8 +54,7 @@ function layoutBlockEvents(events) {
return result
}
export function layoutEvents(events) {
const overlap = (event, block) =>
event.start < block.end && block.start < event.end
const overlap = (event, block) => event.start < block.end && block.start < event.end
events.sort((a, b) => a.start - b.start)
@@ -68,10 +64,7 @@ export function layoutEvents(events) {
if (blocks.length > 0) {
layout = layout.filter(block => !overlap(event, block))
layout.push({
start: Math.min(
event.start,
...blocks.map(block => block.start)
),
start: Math.min(event.start, ...blocks.map(block => block.start)),
end: Math.max(event.end, ...blocks.map(block => block.end)),
events: blocks.flatMap(block => block.events).concat([event]),
})
+52 -77
View File
@@ -1,7 +1,6 @@
import _ from 'lodash'
import { render } from 'preact'
import { useEffect, useState } from 'preact/hooks'
import { differenceInMinutes, startOfDay } from 'date-fns'
// import { ToolOverlay } from './components/ToolOverlay.jsx'
//
@@ -19,18 +18,25 @@ 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'
import {
prettyAulaName,
prettyProfName,
clearOldPersistentStates,
usePersistentState,
} from './utils.jsx'
import { SettingsBar } from './components/SettingsBar.jsx'
// Che fanno queste due righe?
window._ = _
window.dataBuffer = {}
// NOTA: magistrale *non* è quello con i corsi a cavallo
const TIMETABLE_IDS = {
'anno-1': '64a7c1c651f079001d52e9c8',
'anno-2': '6308e2dc09352a0208fefdd9',
'anno-3': '6308e42a1df5cb026699ced4',
'magistrale': '64a7c7091ab813002c5d9ede',
'anno-1': '687e66d62d13e6001edd366d',
'anno-2': '687e681301ee9300198320e6',
'anno-3': '687e68bb01ee9300198320eb',
'magistrale': '687e6a9f96ea080019d439d5',
}
// const DEFAULT_DATE_RANGE = {
@@ -46,15 +52,17 @@ const TIMETABLE_IDS = {
// }
function specialEventPatches(eventi) {
// Il laboratorio del primo anno in realtà è in due canali separati
let i = 1
// Il laboratorio del primo anno in realtà è in due gruppi separati
eventi.forEach(evento => {
if (
evento.nome ===
'LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE'
evento.nome === 'LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE'
) {
evento.nome += ` (${i})`
i++
if (evento.evento.dettagliDidattici[0].partizione.descrizione === 'CORSO A') {
evento.nome += ' (A)'
}
if (evento.evento.dettagliDidattici[0].partizione.descrizione === 'CORSO B') {
evento.nome += ' (B)'
}
}
})
@@ -63,17 +71,12 @@ function specialEventPatches(eventi) {
function formatEvents(timetable) {
return timetable.map(({ nome, dataInizio, dataFine, docenti, aule }) => {
const start = new Date(dataInizio)
const end = new Date(dataFine)
return {
id: nome,
name: _.split(nome, '-', 1)[0].trim(),
day: start.getDay(),
start: differenceInMinutes(start, startOfDay(start)),
end: differenceInMinutes(end, startOfDay(start)),
docenti: docenti.map(({ nome, cognome }) =>
prettyProfName(nome, cognome)
),
start: new Date(dataInizio),
end: new Date(dataFine),
docenti: docenti.map(({ nome, cognome }) => prettyProfName(nome, cognome)),
aule: aule.map(aula => prettyAulaName(aula.codice)),
}
})
@@ -130,7 +133,7 @@ async function loadCalendari(date) {
const timetablesRaw = results.map(timetable =>
specialEventPatches(_.uniqBy(timetable, 'id'))
)
const allRaw = specialEventPatches(_.uniqBy(_.concat(...results), 'id'))
const allRaw = specialEventPatches(_.concat(...results), 'id')
return {
'anno-1': formatEvents(timetablesRaw[0]),
@@ -141,50 +144,19 @@ async function loadCalendari(date) {
}
}
const View = ({
view,
selection,
setSelection,
timetables,
custom,
setCustom,
}) => {
const View = ({ view, selection, setSelection, timetables }) => {
if (view === 'orario') {
return (
<Schedule
selection={selection}
timetables={timetables}
custom={custom}
/>
)
return <Schedule selection={selection} timetables={timetables} />
} else if (view === 'lista') {
return (
<Courses
selection={selection}
setSelection={setSelection}
source={'tutti'}
timetables={timetables}
custom={custom}
isRestrictedList={true}
hideOtherCourses={true}
/>
)
} else if (view === 'custom') {
return (
<div class="custom-events-view">
<textarea
value={custom}
onChange={e => setCustom(e.target.value)}
/>
<p>Esempio di evento personalizzato</p>
<p>
Nome evento [label globale]:
<br />
- Lun 9-11
<br />
- Mar 9:00-11:00
<br />- Gio 8:00-12:00 [label locale]
</p>
</div>
)
} else {
return (
<Courses
@@ -192,7 +164,7 @@ const View = ({
setSelection={setSelection}
source={view}
timetables={timetables}
isRestrictedList={false}
hideOtherCourses={false}
/>
)
}
@@ -208,28 +180,22 @@ const App = ({}) => {
const [date, setDate] = useState(new Date().toISOString())
// Data Sources
const [view, setView] = usePersistentState('view', 'magistrale')
const [view, setView] = usePersistentState('view', 'tutti')
const [timetables, setTimetables] = useState(null)
useEffect(async () => {
setTimetables(await loadCalendari(new Date(date)))
}, [date])
// Selection
const [selectedCourses, setSelectedCourses] = usePersistentState(
'selection',
[]
)
// View Modes
// const [mode, setMode] = usePersistentState('orario.mode', MODE_COURSES)
// Custom Events
const [custom, setCustom] = usePersistentState('custom', [])
// Selection
const [selectedCourses, setSelectedCourses] = usePersistentState('selection', [])
// Menus
const [helpVisible, setHelpVisible] = 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(
'theme',
'light'
@@ -292,18 +258,29 @@ const App = ({}) => {
) : timetables['tutti'].length === 0 ? (
<div class="warning">
<p>
Non esistono corsi per la settimana selezionata:
buone vacanze! 🎉
Non esistono corsi per la settimana selezionata: buone
vacanze! 🎉
</p>
<p>
Per cambiare settimana puoi usare il widget
Calendario (
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 (
In versione mobile, il widget Calendario è situato dentro
il Menu (
<Icon name="menu" />)
</p>
</p> */}
</div>
) : (
<View
@@ -311,8 +288,6 @@ const App = ({}) => {
setSelection={setSelectedCourses}
view={view}
timetables={timetables}
custom={custom}
setCustom={setCustom}
/>
))}
</div>
+11 -50
View File
@@ -530,9 +530,9 @@ body {
}
.content {
height: calc(100dvh - 4rem);
height: calc(100vh - 4rem);
@media screen and (max-width: $device-s-width), (pointer: coarse) {
height: calc(100dvh - 8rem);
height: calc(100vh - 8rem);
}
overflow-y: scroll;
@@ -624,7 +624,7 @@ body {
.schedule-view {
min-height: 100%;
width: 100%;
max-width: 55rem;
max-width: 57rem;
margin: auto;
padding: 0rem 0.5rem;
@@ -644,8 +644,7 @@ body {
border: 1px solid var(--border-600);
border-radius: 10px 10px 0 0;
@media screen and (max-width: $device-s-width),
(pointer: coarse) {
@media screen and (max-width: $device-s-width), (pointer: coarse) {
font-size: 12px;
}
@@ -700,10 +699,7 @@ body {
&.original {
grid-template-columns: auto repeat(5, 1fr);
grid-template-rows: min-content repeat(
var(--time-slots),
1fr
);
grid-template-rows: min-content repeat(var(--time-slots), 1fr);
.transpose-button,
.day-label {
@@ -730,22 +726,16 @@ body {
}
.event-block-wrapper {
grid-row: calc(var(--time-start) + 2) /
calc(var(--time-end) + 2);
grid-row: calc(var(--time-start) + 2) / calc(var(--time-end) + 2);
grid-column: calc(var(--day-position) + 1);
.event-block {
.event-wrapper {
width: calc(100% / var(--of));
height: calc(
100% * var(--size) / var(--block-size)
);
height: calc(100% * var(--size) / var(--block-size));
transform: translateX(calc(100% * var(--index)))
translateY(
calc(
100% * var(--relative-start) /
var(--size)
)
calc(100% * var(--relative-start) / var(--size))
);
}
}
@@ -753,10 +743,7 @@ body {
}
&.transposed {
grid-template-rows: auto repeat(5, 1fr);
grid-template-columns: min-content repeat(
var(--time-slots),
1fr
);
grid-template-columns: min-content repeat(var(--time-slots), 1fr);
.transpose-button,
.day-label {
@@ -792,15 +779,10 @@ body {
.event-block {
.event-wrapper {
height: calc(100% / var(--of));
width: calc(
100% * var(--size) / var(--block-size)
);
width: calc(100% * var(--size) / var(--block-size));
transform: translateY(calc(100% * var(--index)))
translateX(
calc(
100% * var(--relative-start) /
var(--size)
)
calc(100% * var(--relative-start) / var(--size))
);
}
}
@@ -897,27 +879,6 @@ body {
overflow-y: scroll;
}
}
.custom-events-view {
min-height: 100%;
width: 100%;
max-width: 30rem;
margin: auto;
padding: 1rem 1rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
textarea {
width: 100%;
min-height: 20rem;
font-size: 20px;
}
}
}
.overlay {
+6 -68
View File
@@ -1,8 +1,7 @@
import { useEffect, useState } from 'preact/hooks'
import { parse } from 'yaml'
// Calendar
import { useEffect, useState } from 'preact/hooks'
export const WEEK_DAYS = [
'Domenica',
'Lunedì',
@@ -25,12 +24,8 @@ export function hashString(str, seed = 0) {
h1 = Math.imul(h1 ^ ch, 2654435761)
h2 = Math.imul(h2 ^ ch, 1597334677)
}
h1 =
Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
Math.imul(h2 ^ (h2 >>> 13), 3266489909)
h2 =
Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
Math.imul(h1 ^ (h1 >>> 13), 3266489909)
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ 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)
}
@@ -43,9 +38,7 @@ export function prettyCourseName(name) {
.map(word => {
if (word.trim().length === 0) return word
return /(^del|^nel|^di$|^dei$|^con$|^alla$|^per$|^e$|^la$)/.test(
word
)
return /(^del|^nel|^di$|^dei$|^con$|^alla$|^per$|^e$|^la$)/.test(word)
? word
: word[0].toUpperCase() + word.slice(1)
})
@@ -54,6 +47,7 @@ export function prettyCourseName(name) {
.replaceAll('IIi', 'III')
.replaceAll('Iii', 'III')
.replaceAll(/'(.)/g, ({}, letter) => "'" + letter.toUpperCase())
.replaceAll(/\((a|b)\)/g, ({}, letter) => '(' + letter.toUpperCase() + ')')
}
export function prettyAulaName(name) {
@@ -70,62 +64,6 @@ export function prettyProfName(nome, cognome) {
.join('')
}
// Custom events
export function parseCustomEvents(customEventsString) {
try {
const customEvents = []
const parsedEvents = parse(customEventsString)
for (const customCourse in parsedEvents) {
const [_, name, teachers] = /([^\[]*)(?:\[(.*)\])?/.exec(
customCourse
)
const docenti = teachers ? teachers.split(',') : []
for (const customEvent of parsedEvents[customCourse]) {
const [_b, day, startH, startM, endH, endM, label] =
/(lun|lunedì|mar|martedì|mer|mercoledì|gio|giovedì|ven|venerdì)\s*(\d{1,2})(?:\:(\d\d))?\s*-\s*(\d{1,2})(?:\:(\d\d))?\s*(.*)?/i.exec(
customEvent
)
const dayNumber = {
lun: 1,
lunedì: 1,
mar: 2,
martedì: 2,
mer: 3,
mercoledì: 3,
gio: 4,
giovedì: 4,
ven: 5,
venerdì: 5,
}[day.toLowerCase()]
const start = startM
? parseInt(startH) * 60 + parseInt(startM)
: parseInt(startH) * 60
const end = endM
? parseInt(endH) * 60 + parseInt(endM)
: parseInt(endH) * 60
const aule = label ? label.split(',') : []
customEvents.push({
id: name,
name,
docenti,
start,
end,
day: dayNumber,
aule,
})
}
}
return customEvents
} catch (e) {
console.log('ehi', e)
return []
}
}
// JSX
export const withClasses = classes =>