Merge pull request 'dev' (#11) from dev into main
continuous-integration/drone/push Build is passing Details

Reviewed-on: #11
pull/12/head
Fran314 1 year ago
commit c43bdc60e7

@ -0,0 +1,26 @@
import { useRef } from 'preact/hooks'
import { Icon } from './Icon.jsx'
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()}>
<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>
)
}

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

@ -13,7 +13,7 @@ export const Help = ({}) => (
<p> <p>
Una volta compiuta la selezione, è possibile vedere la tabella delle Una volta compiuta la selezione, è possibile vedere la tabella delle
lezioni andando nella visualizzazione Orario ( lezioni andando nella visualizzazione Orario (
<Icon name="calendar_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

@ -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,15 +14,15 @@ 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, icon: true,
}, },
{ {
@ -31,8 +31,8 @@ export const OptionBar = ({ source, setSource }) => {
icon: true, icon: true,
}, },
]} ]}
value={source} value={view}
setValue={setSource} setValue={setView}
/> />
</div> </div>
</div> </div>

@ -0,0 +1,22 @@
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>
)
}

@ -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,8 +16,11 @@ 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">
@ -45,6 +52,9 @@ export const Toolbar = ({
/> />
</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()}>

@ -3,6 +3,7 @@ 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 = ({ export const Courses = ({
source, source,
@ -53,7 +54,7 @@ export const Courses = ({
return ( return (
<div class="course-view" ref={element}> <div class="course-view" ref={element}>
{hideOtherCourses && selection.length === 0 && ( {hideOtherCourses && selection.length === 0 && (
<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

@ -46,7 +46,7 @@ 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

@ -24,6 +24,7 @@ import {
clearOldPersistentStates, clearOldPersistentStates,
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._ = _
@ -36,17 +37,17 @@ const TIMETABLE_IDS = {
'magistrale': '64a7c7091ab813002c5d9ede', 'magistrale': '64a7c7091ab813002c5d9ede',
} }
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 canali separati
@ -79,7 +80,18 @@ function formatEvents(timetable) {
}) })
} }
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 +109,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',
@ -163,12 +175,14 @@ const App = ({}) => {
// 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 [view, setView] = usePersistentState('view', 'magistrale') const [view, setView] = usePersistentState('view', 'magistrale')
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)
@ -202,35 +216,42 @@ const App = ({}) => {
{...{ {...{
source: view, source: view,
setSource: setView, 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: view,
setSource: setView, setView: setView,
onHelp: () => setHelpVisible(true), onHelp: () => setHelpVisible(true),
}} }}
orizzontale orizzontale
/> />
{timetables && (
<div class="content">
<View
selection={selectedCourses}
setSelection={setSelectedCourses}
view={view}
timetables={timetables}
/>
</div>
)} )}
{showMobileMenu && ( <div class="content">
{timetables &&
(showMobileMenu ? (
<HamburgerMenu <HamburgerMenu
{...{ {...{
date,
setDate,
theme, theme,
setTheme, setTheme,
onClose: () => { onClose: () => {
@ -238,7 +259,34 @@ const App = ({}) => {
}, },
}} }}
/> />
)} ) : timetables['tutti'].length === 0 ? (
<div class="warning">
<p>
Non esistono corsi per la settimana selezionata:
buone vacanze! 🎉
</p>
<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={

@ -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,23 +534,13 @@ 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);
} }
.course-view {
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
overflow-y: scroll; overflow-y: scroll;
text-align: center; padding: 1rem 0rem;
gap: 1rem; .warning {
@extend .text-block;
.no-courses-warning {
width: 100%; width: 100%;
display: flex; display: flex;
@ -511,6 +553,19 @@ body {
} }
} }
.course-view {
padding: 0rem 1rem;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100%;
text-align: center;
gap: 1rem;
.wrap-container { .wrap-container {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(30ch, 1fr)); grid-template-columns: repeat(auto-fill, minmax(30ch, 1fr));
@ -567,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%;
@ -790,47 +833,12 @@ body {
} }
} }
} }
} .menu {
// position: absolute;
.overlay { // top: 0;
display: flex; // left: 0;
position: fixed; width: 100%;
min-height: 100%;
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 {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: var(--bg-500); background: var(--bg-500);
@ -883,7 +891,45 @@ 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

Loading…
Cancel
Save