dev #11
@@ -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 { 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 { Icon } from './Icon.jsx'
|
||||
|
||||
export const HamburgerMenu = ({ onClose, theme, setTheme }) => {
|
||||
export const HamburgerMenu = ({ date, setDate, onClose, theme, setTheme }) => {
|
||||
return (
|
||||
<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">
|
||||
<h2>
|
||||
<Icon name="info" />
|
||||
|
||||
@@ -13,7 +13,7 @@ export const Help = ({}) => (
|
||||
<p>
|
||||
Una volta compiuta la selezione, è possibile vedere la tabella delle
|
||||
lezioni andando nella visualizzazione Orario (
|
||||
<Icon name="calendar_month" />)
|
||||
<Icon name="calendar_view_month" />)
|
||||
</p>
|
||||
<p>
|
||||
Per via di eventuali preferenze personali, è possibile cambiare
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CompoundButton } from './CompoundButton.jsx'
|
||||
import { Icon } from './Icon.jsx'
|
||||
|
||||
export const OptionBar = ({ source, setSource }) => {
|
||||
export const OptionBar = ({ view, setView }) => {
|
||||
return (
|
||||
<div class="option-bar">
|
||||
<div class="option-group">
|
||||
@@ -14,15 +14,15 @@ export const OptionBar = ({ source, setSource }) => {
|
||||
{ value: 'magistrale', label: 'M' },
|
||||
{ value: 'tutti', label: 'Tutti' },
|
||||
]}
|
||||
value={source}
|
||||
setValue={setSource}
|
||||
value={view}
|
||||
setValue={setView}
|
||||
/>
|
||||
</div>
|
||||
<CompoundButton
|
||||
options={[
|
||||
{
|
||||
value: 'orario',
|
||||
label: <Icon name="calendar_month" />,
|
||||
label: <Icon name="calendar_view_month" />,
|
||||
icon: true,
|
||||
},
|
||||
{
|
||||
@@ -31,8 +31,8 @@ export const OptionBar = ({ source, setSource }) => {
|
||||
icon: true,
|
||||
},
|
||||
]}
|
||||
value={source}
|
||||
setValue={setSource}
|
||||
value={view}
|
||||
setValue={setView}
|
||||
/>
|
||||
</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 { DatePicker } from './DatePicker.jsx'
|
||||
import { Icon } from './Icon.jsx'
|
||||
|
||||
export const Toolbar = ({
|
||||
source,
|
||||
setSource,
|
||||
onShowMenu,
|
||||
date,
|
||||
setDate,
|
||||
showMobileMenu,
|
||||
setShowMobileMenu,
|
||||
onHelp,
|
||||
theme,
|
||||
setTheme,
|
||||
@@ -12,8 +16,11 @@ export const Toolbar = ({
|
||||
return (
|
||||
<div class="toolbar">
|
||||
<div class="mobile">
|
||||
<button class="flat icon" onClick={onShowMenu}>
|
||||
<Icon name="menu" />
|
||||
<button
|
||||
class="flat icon"
|
||||
onClick={() => setShowMobileMenu(!showMobileMenu)}
|
||||
>
|
||||
<Icon name={`${showMobileMenu ? 'close' : 'menu'}`} />
|
||||
</button>
|
||||
</div>
|
||||
<div class="item logo">
|
||||
@@ -45,6 +52,9 @@ export const Toolbar = ({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-group">
|
||||
<DatePicker date={date} setDate={setDate} />
|
||||
</div>
|
||||
<div class="option-group">
|
||||
<div class="item option">
|
||||
<button class="icon" onClick={() => window.print()}>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { format } from 'date-fns'
|
||||
import _ from 'lodash'
|
||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { prettyCourseName, WEEK_DAYS } from '../../utils.jsx'
|
||||
import { Icon } from '../Icon.jsx'
|
||||
|
||||
export const Courses = ({
|
||||
source,
|
||||
@@ -53,7 +54,7 @@ export const Courses = ({
|
||||
return (
|
||||
<div class="course-view" ref={element}>
|
||||
{hideOtherCourses && selection.length === 0 && (
|
||||
<div class="no-courses-warning">
|
||||
<div class="warning">
|
||||
<p>Non hai ancora selezionato nessun corso.</p>
|
||||
<p>
|
||||
Clicca sui corsi nelle altre visuali per selezionarli e
|
||||
|
||||
@@ -46,7 +46,7 @@ const TransposePopup = ({ onClose }) => {
|
||||
|
||||
const NoCourseWarning = () => {
|
||||
return (
|
||||
<div class="no-courses-warning">
|
||||
<div class="warning">
|
||||
<p>Non hai ancora selezionato nessun corso.</p>
|
||||
<p>
|
||||
Clicca sui corsi nelle altre visuali per selezionarli e
|
||||
|
||||
+89
-41
@@ -24,6 +24,7 @@ import {
|
||||
clearOldPersistentStates,
|
||||
usePersistentState,
|
||||
} from './utils.jsx'
|
||||
import { SettingsBar } from './components/SettingsBar.jsx'
|
||||
|
||||
// Che fanno queste due righe?
|
||||
window._ = _
|
||||
@@ -36,17 +37,17 @@ const TIMETABLE_IDS = {
|
||||
'magistrale': '64a7c7091ab813002c5d9ede',
|
||||
}
|
||||
|
||||
const DEFAULT_DATE_RANGE = {
|
||||
from: '2023-10-09T00:00:00.000Z',
|
||||
to: '2023-10-14T00:00:00.000Z',
|
||||
}
|
||||
// const DEFAULT_DATE_RANGE = {
|
||||
// from: '2023-10-09T00:00:00.000Z',
|
||||
// to: '2023-10-14T00:00:00.000Z',
|
||||
// }
|
||||
|
||||
const DATE_RANGES = {
|
||||
'64a7c1c651f079001d52e9c8': DEFAULT_DATE_RANGE,
|
||||
'6308e2dc09352a0208fefdd9': DEFAULT_DATE_RANGE,
|
||||
'6308e42a1df5cb026699ced4': DEFAULT_DATE_RANGE,
|
||||
'64a7c7091ab813002c5d9ede': DEFAULT_DATE_RANGE,
|
||||
}
|
||||
// const DATE_RANGES = {
|
||||
// '64a7c1c651f079001d52e9c8': DEFAULT_DATE_RANGE,
|
||||
// '6308e2dc09352a0208fefdd9': DEFAULT_DATE_RANGE,
|
||||
// '6308e42a1df5cb026699ced4': DEFAULT_DATE_RANGE,
|
||||
// '64a7c7091ab813002c5d9ede': DEFAULT_DATE_RANGE,
|
||||
// }
|
||||
|
||||
function specialEventPatches(eventi) {
|
||||
// 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) {
|
||||
// Almost directly copy-pasted from Chrome Dev Tools
|
||||
const req = await fetch(
|
||||
@@ -97,8 +109,8 @@ async function loadCalendari() {
|
||||
linkCalendarioId: id,
|
||||
clienteId: '628de8b9b63679f193b87046',
|
||||
pianificazioneTemplate: false,
|
||||
dataInizio: DATE_RANGES[id].from,
|
||||
dataFine: DATE_RANGES[id].to,
|
||||
dataInizio: monday.toISOString(),
|
||||
dataFine: saturday.toISOString(),
|
||||
}),
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
@@ -163,12 +175,14 @@ const App = ({}) => {
|
||||
// Use any random string of your choice
|
||||
// clearOldPersistentStates('e73cba02')
|
||||
|
||||
const [date, setDate] = useState(new Date().toISOString())
|
||||
|
||||
// Data Sources
|
||||
const [view, setView] = usePersistentState('view', 'magistrale')
|
||||
const [timetables, setTimetables] = useState(null)
|
||||
useEffect(async () => {
|
||||
setTimetables(await loadCalendari())
|
||||
}, [])
|
||||
setTimetables(await loadCalendari(new Date(date)))
|
||||
}, [date])
|
||||
|
||||
// View Modes
|
||||
// const [mode, setMode] = usePersistentState('orario.mode', MODE_COURSES)
|
||||
@@ -202,43 +216,77 @@ const App = ({}) => {
|
||||
{...{
|
||||
source: view,
|
||||
setSource: setView,
|
||||
onShowMenu: () => setShowMobileMenu(true),
|
||||
date: date,
|
||||
setDate: setDate,
|
||||
showMobileMenu: showMobileMenu,
|
||||
setShowMobileMenu: setShowMobileMenu,
|
||||
onHelp: () => setHelpVisible(true),
|
||||
theme,
|
||||
setTheme,
|
||||
}}
|
||||
/>
|
||||
<OptionBar
|
||||
{...{
|
||||
source: view,
|
||||
setSource: setView,
|
||||
onHelp: () => setHelpVisible(true),
|
||||
}}
|
||||
orizzontale
|
||||
/>
|
||||
|
||||
{timetables && (
|
||||
<div class="content">
|
||||
<View
|
||||
selection={selectedCourses}
|
||||
setSelection={setSelectedCourses}
|
||||
view={view}
|
||||
timetables={timetables}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showMobileMenu && (
|
||||
<HamburgerMenu
|
||||
{showMobileMenu ? (
|
||||
<SettingsBar
|
||||
{...{
|
||||
theme,
|
||||
setTheme,
|
||||
onClose: () => {
|
||||
setShowMobileMenu(false)
|
||||
},
|
||||
date,
|
||||
setDate,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<OptionBar
|
||||
{...{
|
||||
view: view,
|
||||
setView: setView,
|
||||
onHelp: () => setHelpVisible(true),
|
||||
}}
|
||||
orizzontale
|
||||
/>
|
||||
)}
|
||||
|
||||
<div class="content">
|
||||
{timetables &&
|
||||
(showMobileMenu ? (
|
||||
<HamburgerMenu
|
||||
{...{
|
||||
date,
|
||||
setDate,
|
||||
theme,
|
||||
setTheme,
|
||||
onClose: () => {
|
||||
setShowMobileMenu(false)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
) : 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 && (
|
||||
<Popup
|
||||
title={
|
||||
|
||||
+140
-94
@@ -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
|
||||
|
||||
.panel {
|
||||
@@ -444,7 +485,8 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.option-bar {
|
||||
.option-bar,
|
||||
.settings-bar {
|
||||
@extend .panel;
|
||||
|
||||
padding: 0.5rem;
|
||||
@@ -469,11 +511,21 @@ body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.option-group {
|
||||
.option-group,
|
||||
.settings-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
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,35 +534,38 @@ body {
|
||||
@media screen and (max-width: $device-s-width), (pointer: coarse) {
|
||||
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 {
|
||||
padding: 1rem;
|
||||
padding: 0rem 1rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
height: 100%;
|
||||
|
||||
overflow-y: scroll;
|
||||
min-height: 100%;
|
||||
|
||||
text-align: center;
|
||||
|
||||
gap: 1rem;
|
||||
|
||||
.no-courses-warning {
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.wrap-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(30ch, 1fr));
|
||||
@@ -567,30 +622,18 @@ body {
|
||||
}
|
||||
|
||||
.schedule-view {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
max-width: 57rem;
|
||||
margin: auto;
|
||||
|
||||
padding: 1rem 0.5rem;
|
||||
padding: 0rem 0.5rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
.no-courses-warning {
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.schedule-card {
|
||||
width: 100%;
|
||||
|
||||
@@ -790,6 +833,70 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
background: var(--bg-500);
|
||||
|
||||
z-index: 10;
|
||||
|
||||
.header {
|
||||
height: 4rem;
|
||||
|
||||
padding: 0.75rem 1rem 0.75rem 0.75rem;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
border-bottom: 1px solid var(--border-500);
|
||||
|
||||
.option-group {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
padding: 1rem;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
||||
gap: 1rem;
|
||||
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
position: relative;
|
||||
|
||||
width: calc(100% - 2rem);
|
||||
height: 1px;
|
||||
left: 1rem;
|
||||
|
||||
background: var(--border-500);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.help {
|
||||
@extend .text-block;
|
||||
|
||||
padding: 1rem 1rem 2.5rem;
|
||||
|
||||
height: 100%;
|
||||
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
@@ -825,67 +932,6 @@ body {
|
||||
animation: fade-in 150ms ease-in forwards;
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background: var(--bg-500);
|
||||
|
||||
z-index: 10;
|
||||
|
||||
.header {
|
||||
height: 4rem;
|
||||
|
||||
padding: 0.75rem 1rem 0.75rem 0.75rem;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
border-bottom: 1px solid var(--border-500);
|
||||
|
||||
.option-group {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
padding: 1rem;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
||||
gap: 1rem;
|
||||
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
position: relative;
|
||||
|
||||
width: calc(100% - 2rem);
|
||||
height: 1px;
|
||||
left: 1rem;
|
||||
|
||||
background: var(--border-500);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.help {
|
||||
@extend .text-block;
|
||||
|
||||
padding: 1rem 1rem 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
// not on mobile
|
||||
|
||||
@media screen and (min-width: $device-s-width) and (pointer: fine) {
|
||||
|
||||
Reference in New Issue
Block a user