Compare commits

...

15 Commits

Author SHA1 Message Date
Antonio De Lucreziis 39ccb715db Merge pull request 'chore: updated calendar ids to first semester of 2024/2025' (#16) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #16
2 months ago
Antonio De Lucreziis d929aa6cfe chore: updated calendar ids to first semester of 2024/2025 2 months ago
Antonio De Lucreziis db72898471 Merge pull request 'chore: empty commit to trigger deploy' (#15) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #15
8 months ago
Antonio De Lucreziis 4d95a1a633 chore: empty commit to trigger deploy 8 months ago
Antonio De Lucreziis 65b800806f Merge pull request 'Fixed iOS date picker bug' (#14) from dev into main
Reviewed-on: #14
8 months ago
Antonio De Lucreziis 935309147c cmd: prettier -w src/ 8 months ago
Antonio De Lucreziis fb551fb719 fix: safari ios showPicker() bug 8 months ago
Francesco Minnocci c731d9c8b9 Merge pull request 'chore: fix LIMCO courses' names' (#13) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #13
9 months ago
Francesco Minnocci d9d2aaed11
chore: fix LIMCO courses' names 9 months ago
Francesco Minnocci 7bbd9ba139 Merge pull request 'chore: update calendar IDs for the 2nd semester' (#12) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #12
9 months ago
Francesco Minnocci 62a9b80b21
chore: update calendar IDs for the 2nd semester 9 months ago
Fran314 c43bdc60e7 Merge pull request 'dev' (#11) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #11
1 year ago
Antonio De Lucreziis bdbf031bce Merge pull request 'fix: rimosso un console.log' (#8) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #8
1 year ago
Fran314 2d4a3ce664 Merge pull request 'reintrodotta visualizzazione lista rimossa nel commit precedente' (#7) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #7
1 year ago
Fran314 22ce830c80 Merge pull request 'dev' (#6) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #6
1 year ago

@ -0,0 +1,9 @@
{
"printWidth": 90,
"singleQuote": true,
"quoteProps": "consistent",
"tabWidth": 4,
"semi": false,
"arrowParens": "avoid",
"proseWrap": "always"
}

@ -1,12 +1,19 @@
import { useRef } from 'preact/hooks' import { useRef } from 'preact/hooks'
import { Icon } from './Icon.jsx' import { Icon } from './Icon.jsx'
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
export const DatePicker = ({ date, setDate }) => { export const DatePicker = ({ date, setDate }) => {
const input = useRef() const input = useRef()
const [year, month, day] = date.split('T')[0].split('-') const [year, month, day] = date.split('T')[0].split('-')
return ( return (
<div class="date-picker" onClick={() => input.current.showPicker()}> <div
class="date-picker"
onClick={() =>
isSafari ? input.current.focus() : input.current.showPicker()
}
>
<input <input
ref={input} ref={input}
type="date" type="date"

@ -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">

@ -4,33 +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" />) <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> <p>
È anche possibile visualizzare in uno specchietto riassuntivo È anche possibile visualizzare in uno specchietto riassuntivo soltanto i corsi
soltanto i corsi selezionati, andando nella visualizzazione Lista ( selezionati, andando nella visualizzazione Lista (
<Icon name="list" />) <Icon name="list" />)
</p> </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 &amp; Contatti</h3> <h3>Bug &amp; Contatti</h3>
<p> <p>

@ -7,13 +7,9 @@ export const SettingsBar = ({ theme, setTheme, date, setDate }) => {
<div class="settings-group"> <div class="settings-group">
<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>
<DatePicker date={date} setDate={setDate} /> <DatePicker date={date} setDate={setDate} />
</div> </div>

@ -24,8 +24,7 @@ export const Toolbar = ({
</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">
@ -64,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">

@ -72,27 +72,18 @@ export const Courses = ({
} }
data-course-id={id} data-course-id={id}
onClick={() => { onClick={() => {
if (!selectionSet.has(id)) if (!selectionSet.has(id)) setSelection([...selection, id])
setSelection([...selection, id]) else setSelection(selection.filter(selId => selId !== id))
else
setSelection(
selection.filter(selId => selId !== id)
)
}} }}
> >
<div class="title"> <div class="title">{prettyCourseName(courseEvents[0].name)}</div>
{prettyCourseName(courseEvents[0].name)} <div class="docenti">{profsPerCourse[id].join(', ')}</div>
</div>
<div class="docenti">
{profsPerCourse[id].join(', ')}
</div>
<div class="events"> <div class="events">
{courseEvents.map(course => ( {courseEvents.map(course => (
<div> <div>
{WEEK_DAYS[course.start.getDay()]}{' '} {WEEK_DAYS[course.start.getDay()]}{' '}
{format(course.start, 'H:mm')}&ndash; {format(course.start, 'H:mm')}&ndash;
{format(course.end, 'H:mm')}{' '} {format(course.end, 'H:mm')} {course.aule.join(', ')}
{course.aule.join(', ')}
</div> </div>
))} ))}
</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> <p>A grande richiesta popolare abbiamo trasposto la tabella dell'orario!</p>
A grande richiesta popolare abbiamo trasposto la tabella
dell'orario!
</p>
<p> <p>
Assicurati quindi di leggerla correttamente (dall'alto verso il Assicurati quindi di leggerla correttamente (dall'alto verso il basso
basso invece che da sinistra verso destra). invece che da sinistra verso destra).
</p> </p>
<p> <p>
Se preferisci utilizzare la versione vecchia, puoi utilizzare il Se preferisci utilizzare la versione vecchia, puoi utilizzare il pulsante
pulsante Trasponi{' '} Trasponi <Icon name="switch_left" style="transform: rotate(-45deg)" />{' '}
<Icon name="switch_left" style="transform: rotate(-45deg)" />{' '} nell'origine della tabella per trasporla. Questa scelta verrà salvata nei
nell'origine della tabella per trasporla. Questa scelta verrà cookie e verrà ricordata in futuro
salvata nei cookie e verrà ricordata in futuro
</p> </p>
</Popup> </Popup>
) )
@ -49,8 +40,8 @@ const NoCourseWarning = () => {
<div class="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>
) )
@ -75,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">
@ -118,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 => (
@ -215,20 +198,9 @@ export const Schedule = ({ timetables, selection }) => {
'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)

@ -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]),
}) })

@ -31,10 +31,10 @@ window._ = _
window.dataBuffer = {} window.dataBuffer = {}
const TIMETABLE_IDS = { const TIMETABLE_IDS = {
'anno-1': '64a7c1c651f079001d52e9c8', 'anno-1': '667e88275e9623041f0e43d4',
'anno-2': '6308e2dc09352a0208fefdd9', 'anno-2': '667e89055e9623041f0e43d6',
'anno-3': '6308e42a1df5cb026699ced4', 'anno-3': '667e89fcf748ed0415a11dcc',
'magistrale': '64a7c7091ab813002c5d9ede', 'magistrale': '667ebae63379a3046517ffd4',
} }
// const DEFAULT_DATE_RANGE = { // const DEFAULT_DATE_RANGE = {
@ -50,15 +50,17 @@ const TIMETABLE_IDS = {
// } // }
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.docenti[0].nome === 'GIOVANNI') {
i++ evento.nome += ' (A)'
}
if (evento.docenti[0].nome === 'PAOLO') {
evento.nome += ' (B)'
}
} }
}) })
@ -72,9 +74,7 @@ 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)),
} }
}) })
@ -131,7 +131,7 @@ async function loadCalendari(date) {
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]),
@ -188,10 +188,7 @@ const App = ({}) => {
// 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)
@ -262,16 +259,15 @@ const App = ({}) => {
) : timetables['tutti'].length === 0 ? ( ) : timetables['tutti'].length === 0 ? (
<div class="warning"> <div class="warning">
<p> <p>
Non esistono corsi per la settimana selezionata: Non esistono corsi per la settimana selezionata: buone
buone vacanze! 🎉 vacanze! 🎉
</p> </p>
<p> <p>
Per cambiare settimana puoi usare il widget Per cambiare settimana puoi usare il widget Calendario (
Calendario (
<Icon name="calendar_month" />) in alto a destra <Icon name="calendar_month" />) in alto a destra
<br /> <br />
In versione mobile, il widget Calendario è In versione mobile, il widget Calendario è situato dentro
situato dentro il Menu ( il Menu (
<Icon name="menu" />) <Icon name="menu" />)
</p> </p>
</div> </div>

@ -644,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;
} }
@ -700,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 {
@ -730,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)
)
); );
} }
} }
@ -753,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 {
@ -792,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)
)
); );
} }
} }

@ -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) {

Loading…
Cancel
Save