diff --git a/src/components/HamburgerMenu.jsx b/src/components/HamburgerMenu.jsx index 5f9ff71..e3df2d2 100644 --- a/src/components/HamburgerMenu.jsx +++ b/src/components/HamburgerMenu.jsx @@ -4,57 +4,57 @@ import { Help } from './Help.jsx' import { Icon } from './Icon.jsx' export const HamburgerMenu = ({ onClose, mode, setMode, source, setSource, theme, setTheme }) => { - return ( - +
*/} +
+

+ + Guida +

+ +
+ + ) } diff --git a/src/components/Optionbar.jsx b/src/components/Optionbar.jsx new file mode 100644 index 0000000..8333016 --- /dev/null +++ b/src/components/Optionbar.jsx @@ -0,0 +1,41 @@ +import { CompoundButton } from './CompoundButton.jsx' +import { MODE_COURSE, MODE_SCHEDULE, MODE_WORKWEEK, MODE_WORKWEEK_GRID } from './EventsView.jsx' +import { Icon } from './Icon.jsx' + +export const Optionbar = ({ mode, setMode, source, setSource, onHelp }) => { + return ( +
+
+
+ }, + ]} + value={source} + setValue={setSource} + /> +
+
+
+
+ }, + { + value: MODE_WORKWEEK_GRID, + label: , + }, + { value: MODE_SCHEDULE, label: }, + ]} + value={mode} + setValue={setMode} + /> +
+
+
+ ) +} diff --git a/src/main.jsx b/src/main.jsx index cd033fe..8765eec 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -9,186 +9,196 @@ import { Help } from './components/Help.jsx' import { Icon } from './components/Icon.jsx' import { Popup } from './components/Popup.jsx' import { Toolbar } from './components/Toolbar.jsx' +import { Optionbar } from './components/Optionbar.jsx' window._ = _ window.dataBuffer = {} const CALENDAR_IDS = { - 'anno-1': ['6308cfcb1df5cb026699ce32'], - 'anno-2': ['6308e2dc09352a0208fefdd9'], - 'anno-3': ['6308e42a1df5cb026699ced4'], - 'magistrale': ['6308e8ea0c34e703bb1f7e85'], - 'tutti': [ - '6308cfcb1df5cb026699ce32', - '6308e2dc09352a0208fefdd9', - '6308e42a1df5cb026699ced4', - '6308e8ea0c34e703bb1f7e85', - ], + 'anno-1': ['6308cfcb1df5cb026699ce32'], + 'anno-2': ['6308e2dc09352a0208fefdd9'], + 'anno-3': ['6308e42a1df5cb026699ced4'], + 'magistrale': ['6308e8ea0c34e703bb1f7e85'], + 'tutti': [ + '6308cfcb1df5cb026699ce32', + '6308e2dc09352a0208fefdd9', + '6308e42a1df5cb026699ced4', + '6308e8ea0c34e703bb1f7e85', + ], } async function loadEventi(ids) { - const calendari = await Promise.all( - ids.map(async id => { - // Almost directly copy-pasted from Chrome Dev Tools - const req = await fetch( - 'https://apache.prod.up.cineca.it/api/Impegni/getImpegniCalendarioPubblico', - { - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-site', - }, - body: JSON.stringify({ - mostraImpegniAnnullati: true, - mostraIndisponibilitaTotali: false, - linkCalendarioId: id, - clienteId: '628de8b9b63679f193b87046', - pianificazioneTemplate: false, - dataInizio: '2022-10-02T22:00:00.000Z', - dataFine: '2022-10-07T22:00:00.000Z', - }), - method: 'POST', - mode: 'cors', - credentials: 'omit', - } - ) - - return await req.json() - }) - ) - - // console.log(calendari) - - if (ids.length === 1) { - return calendari[0] - } - - return _.uniqBy(_.concat(...calendari), 'id') + const calendari = await Promise.all( + ids.map(async id => { + // Almost directly copy-pasted from Chrome Dev Tools + const req = await fetch( + 'https://apache.prod.up.cineca.it/api/Impegni/getImpegniCalendarioPubblico', + { + headers: { + 'content-type': 'application/json;charset=UTF-8', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-site', + }, + body: JSON.stringify({ + mostraImpegniAnnullati: true, + mostraIndisponibilitaTotali: false, + linkCalendarioId: id, + clienteId: '628de8b9b63679f193b87046', + pianificazioneTemplate: false, + dataInizio: '2022-10-02T22:00:00.000Z', + dataFine: '2022-10-07T22:00:00.000Z', + }), + method: 'POST', + mode: 'cors', + credentials: 'omit', + } + ) + + return await req.json() + }) + ) + + // console.log(calendari) + + if (ids.length === 1) { + return calendari[0] + } + + return _.uniqBy(_.concat(...calendari), 'id') } const App = ({}) => { - // Data Sources - const [source, setSource] = useState('anno-1') - const [eventi, setEventi] = useState([]) - - // View Modes - const [mode, setMode] = useState(MODE_WORKWEEK_GRID) - - // Selection - const [selectedCourses, setSelectedCourses] = useState([]) - const [hideOtherCourses, setHideOtherCourses] = useState(false) - - // Menus - const [helpVisible, setHelpVisible] = useState(false) - const [showMobileMenu, setShowMobileMenu] = useState(false) - - useEffect(async () => { - console.log('source changed') - const eventi = await loadEventi(CALENDAR_IDS[source]) - - window.dataBuffer[source] = eventi - - setEventi(eventi) - }, [source]) - - const groupIds = new Set(eventi.map(e => e.nome)) - const toolOverlayVisible = - selectedCourses.length > 0 && selectedCourses.filter(id => groupIds.has(id)).length > 0 - - useEffect(() => { - console.log('course length changed') - const groupIds = new Set(eventi.map(e => e.nome)) - - if ( - selectedCourses.length === 0 || - selectedCourses.filter(id => groupIds.has(id)).length === 0 - ) { - setHideOtherCourses(false) - } - }, [eventi, selectedCourses.length]) - - const [theme, setTheme] = useState( - localStorage.getItem('theme') ?? - (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') - ) - - document.body.classList.toggle('dark-mode', theme === 'dark') - localStorage.setItem('theme', theme) - - return ( - <> - setShowMobileMenu(true), - onHelp: () => setHelpVisible(true), - theme, - setTheme, - }} - /> - ({ - id: nome, - name: _.split(nome, '-', 1)[0].trim(), - start: new Date(dataInizio), - end: new Date(dataFine), - docenti: docenti.map(({ nome, cognome }) => - _.startCase(_.lowerCase(nome) + ' ' + _.lowerCase(cognome)) - ), - aula: _.startCase(aule[0].codice.toLowerCase()).replace( - /([A-Z]) ([1-9])/, - '$1$2' - ), - }))} - /> - {toolOverlayVisible && ( - setHideOtherCourses(s => !s)} - onClose={() => { - setSelectedCourses([]) - setHideOtherCourses(false) - }} - /> - )} - {showMobileMenu && ( - { - setShowMobileMenu(false) - }, - }} - /> - )} - {helpVisible && ( - - Guida - - } - onClose={() => setHelpVisible(false)} - > - - - )} - - ) + // Data Sources + const [source, setSource] = useState('anno-1') + const [eventi, setEventi] = useState([]) + + // View Modes + const [mode, setMode] = useState(MODE_WORKWEEK_GRID) + + // Selection + const [selectedCourses, setSelectedCourses] = useState([]) + const [hideOtherCourses, setHideOtherCourses] = useState(false) + + // Menus + const [helpVisible, setHelpVisible] = useState(false) + const [showMobileMenu, setShowMobileMenu] = useState(false) + + useEffect(async () => { + console.log('source changed') + const eventi = await loadEventi(CALENDAR_IDS[source]) + + window.dataBuffer[source] = eventi + + setEventi(eventi) + }, [source]) + + const groupIds = new Set(eventi.map(e => e.nome)) + const toolOverlayVisible = + selectedCourses.length > 0 && selectedCourses.filter(id => groupIds.has(id)).length > 0 + + useEffect(() => { + console.log('course length changed') + const groupIds = new Set(eventi.map(e => e.nome)) + + if ( + selectedCourses.length === 0 || + selectedCourses.filter(id => groupIds.has(id)).length === 0 + ) { + setHideOtherCourses(false) + } + }, [eventi, selectedCourses.length]) + + const [theme, setTheme] = useState( + localStorage.getItem('theme') ?? + (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') + ) + + document.body.classList.toggle('dark-mode', theme === 'dark') + localStorage.setItem('theme', theme) + + return ( + <> + setShowMobileMenu(true), + onHelp: () => setHelpVisible(true), + theme, + setTheme, + }} + /> + setHelpVisible(true), + }} + /> + ({ + id: nome, + name: _.split(nome, '-', 1)[0].trim(), + start: new Date(dataInizio), + end: new Date(dataFine), + docenti: docenti.map(({ nome, cognome }) => + _.startCase(_.lowerCase(nome) + ' ' + _.lowerCase(cognome)) + ), + aula: _.startCase(aule[0].codice.toLowerCase()).replace( + /([A-Z]) ([1-9])/, + '$1$2' + ), + }))} + /> + {toolOverlayVisible && ( + setHideOtherCourses(s => !s)} + onClose={() => { + setSelectedCourses([]) + setHideOtherCourses(false) + }} + /> + )} + {showMobileMenu && ( + { + setShowMobileMenu(false) + }, + }} + /> + )} + {helpVisible && ( + + Guida + + } + onClose={() => setHelpVisible(false)} + > + + + )} + + ) } render(, document.body) diff --git a/src/styles/main.scss b/src/styles/main.scss index 5d44a3d..79cd77d 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -28,35 +28,34 @@ html { --accent-500: #6cc16c; --accent-900: #244624; - - --bubble-red: hsl(359, 100%, 92%); - --bubble-purple: hsl(274, 100%, 92%); - --bubble-blue: hsl(241, 100%, 92%); - --bubble-yellow: hsl(50, 100%, 92%); - --bubble-green: hsl(125, 100%, 92%); - --bubble-orange: hsl(25, 100%, 92%); - --bubble-lightlightblue: hsl(176, 100%, 92%); - --bubble-lightblue: hsl(198, 100%, 92%); - - --bubble-highlight-red: hsl(359, 100%, 85%); - --bubble-highlight-purple: hsl(274, 100%, 85%); - --bubble-highlight-blue: hsl(241, 100%, 85%); - --bubble-highlight-yellow: hsl(50, 100%, 85%); - --bubble-highlight-green: hsl(125, 100%, 85%); - --bubble-highlight-orange: hsl(25, 100%, 85%); - --bubble-highlight-lightlightblue: hsl(176, 100%, 85%); - --bubble-highlight-lightblue: hsl(198, 100%, 85%); - - --bubble-border-red: hsl(359, 75%, 51%); - --bubble-border-purple: hsl(274, 75%, 51%); - --bubble-border-blue: hsl(241, 75%, 51%); - --bubble-border-yellow: hsl(50, 75%, 51%); - --bubble-border-green: hsl(125, 75%, 51%); - --bubble-border-orange: hsl(25, 75%, 51%); - --bubble-border-lightlightblue: hsl(176, 75%, 51%); - --bubble-border-lightblue: hsl(198, 75%, 51%); - - --bold-on-dark: 300; + --bubble-red: hsl(359, 100%, 92%); + --bubble-purple: hsl(274, 100%, 92%); + --bubble-blue: hsl(241, 100%, 92%); + --bubble-yellow: hsl(50, 100%, 92%); + --bubble-green: hsl(125, 100%, 92%); + --bubble-orange: hsl(25, 100%, 92%); + --bubble-lightlightblue: hsl(176, 100%, 92%); + --bubble-lightblue: hsl(198, 100%, 92%); + + --bubble-highlight-red: hsl(359, 100%, 85%); + --bubble-highlight-purple: hsl(274, 100%, 85%); + --bubble-highlight-blue: hsl(241, 100%, 85%); + --bubble-highlight-yellow: hsl(50, 100%, 85%); + --bubble-highlight-green: hsl(125, 100%, 85%); + --bubble-highlight-orange: hsl(25, 100%, 85%); + --bubble-highlight-lightlightblue: hsl(176, 100%, 85%); + --bubble-highlight-lightblue: hsl(198, 100%, 85%); + + --bubble-border-red: hsl(359, 75%, 51%); + --bubble-border-purple: hsl(274, 75%, 51%); + --bubble-border-blue: hsl(241, 75%, 51%); + --bubble-border-yellow: hsl(50, 75%, 51%); + --bubble-border-green: hsl(125, 75%, 51%); + --bubble-border-orange: hsl(25, 75%, 51%); + --bubble-border-lightlightblue: hsl(176, 75%, 51%); + --bubble-border-lightblue: hsl(198, 75%, 51%); + + --bold-on-dark: 300; } body.dark-mode { @@ -74,32 +73,31 @@ body.dark-mode { --accent-500: hsl(269, 40%, 70%); --accent-900: hsl(269, 30%, 90%); - - --bubble-red: hsl(359, 40%, 25%); - --bubble-purple: hsl(274, 40%, 25%); - --bubble-blue: hsl(241, 40%, 25%); - --bubble-yellow: hsl(50, 40%, 25%); - --bubble-green: hsl(125, 40%, 25%); - --bubble-orange: hsl(25, 40%, 25%); - --bubble-lightblue: hsl(176, 40%, 25%); - - --bubble-border-red: hsl(359, 75%, 51%); - --bubble-border-purple: hsl(274, 75%, 51%); - --bubble-border-blue: hsl(241, 75%, 51%); - --bubble-border-yellow: hsl(50, 75%, 51%); - --bubble-border-green: hsl(125, 75%, 51%); - --bubble-border-orange: hsl(25, 75%, 51%); - --bubble-border-lightblue: hsl(176, 75%, 51%); - - --bubble-highlight-red: hsl(359, 40%, 31%); - --bubble-highlight-purple: hsl(274, 40%, 31%); - --bubble-highlight-blue: hsl(241, 40%, 31%); - --bubble-highlight-yellow: hsl(50, 40%, 31%); - --bubble-highlight-green: hsl(125, 40%, 31%); - --bubble-highlight-orange: hsl(25, 40%, 31%); - --bubble-highlight-lightblue: hsl(176, 40%, 31%); - - --bold-on-dark: 500; + --bubble-red: hsl(359, 40%, 25%); + --bubble-purple: hsl(274, 40%, 25%); + --bubble-blue: hsl(241, 40%, 25%); + --bubble-yellow: hsl(50, 40%, 25%); + --bubble-green: hsl(125, 40%, 25%); + --bubble-orange: hsl(25, 40%, 25%); + --bubble-lightblue: hsl(176, 40%, 25%); + + --bubble-border-red: hsl(359, 75%, 51%); + --bubble-border-purple: hsl(274, 75%, 51%); + --bubble-border-blue: hsl(241, 75%, 51%); + --bubble-border-yellow: hsl(50, 75%, 51%); + --bubble-border-green: hsl(125, 75%, 51%); + --bubble-border-orange: hsl(25, 75%, 51%); + --bubble-border-lightblue: hsl(176, 75%, 51%); + + --bubble-highlight-red: hsl(359, 40%, 31%); + --bubble-highlight-purple: hsl(274, 40%, 31%); + --bubble-highlight-blue: hsl(241, 40%, 31%); + --bubble-highlight-yellow: hsl(50, 40%, 31%); + --bubble-highlight-green: hsl(125, 40%, 31%); + --bubble-highlight-orange: hsl(25, 40%, 31%); + --bubble-highlight-lightblue: hsl(176, 40%, 31%); + + --bold-on-dark: 500; } $device-s-width: 600px; @@ -450,9 +448,38 @@ body { } } } +.optionbar { + @extend .panel; + + padding: 1rem 0.75rem 1rem 1rem; + + border-radius: 0; + border: none; + border-bottom: 1px solid var(--border-500); + + height: 4rem; + + display: none; + align-items: center; + justify-content: space-between; + + @media screen and (max-width: $device-s-width), (pointer: coarse) { + display: flex; + } + + .option-group { + display: flex; + align-items: center; + + gap: 0.75rem; + } +} .events-view { height: calc(100vh - 4rem); + @media screen and (max-width: $device-s-width), (pointer: coarse) { + height: calc(100vh - 8rem); + } .course-view { padding: 1rem; @@ -880,109 +907,108 @@ body { height: 100%; overflow: scroll; - .grid { - width: 100%; - max-width: 55rem; - display: grid; - grid-template-columns: 3rem repeat(var(--time-length), 1fr); - grid-template-rows: 2rem repeat(var(--days-length), 1fr); - border: 2px solid var(--border-500); - border-radius: 10px 10px 0 0; - - - @media screen and (max-width: $device-s-width), (pointer: coarse) { - font-size: 12px; - } - - .time { - display: flex; - align-items: center; - justify-content: center; - padding: 0.5rem; - grid-row: 1; - grid-column: var(--offset) / span 2; - } - .day-name { - display: flex; - align-items: center; - justify-content: center; - padding: 0.5rem; - grid-column: 1; - grid-row: var(--line); - } - - .event { - display: flex; - flex-direction: row; - @media screen and (max-width: $device-s-width), (pointer: coarse) { - flex-direction: column; - } - gap: 0.25rem; - align-items: center; - justify-content: center; - margin: 0.25rem; - padding: 0.5rem; - grid-row: var(--line); - grid-column: var(--offset) / span var(--length); - background-color: var(--color); - border: 3px solid var(--color); - border-radius: 10px; - font-weight: var(--bold-on-dark); - text-align: center; - - &.highlight { - background-color: var(--highlight-color); - border: 3px solid var(--highlight-color); - } - &.selected { - border: 3px solid var(--border-color); - } - } - - .hline { - grid-column: 1 / 13; - grid-row: var(--line); - height: 0px; - border-bottom: 1px solid var(--border-500); - } - - .vline { - grid-column: var(--offset); - grid-row: 1 / calc(2 + var(--days-length)); - width: 0px; - border-right: 1px dashed var(--border-500); - } - } - .legend { - max-width: 55rem; - display: grid; - grid-template-columns: min-content 1fr; - border: 2px solid var(--border-500); - border-radius: 0 0 10px 10px; - border-top: none; - padding: 1rem; - gap: 0.5rem 1rem; - align-items: center; - justify-content: center; - width: 100%; - - .name { - width: 100%; - } - - .color { - display: flex; - justify-content: center; - align-items: center; - font-weight: 500; - width: 2rem; - height: 1.5rem; - background-color: var(--color); - border: 2px solid var(--border-color); - border-radius: 10px; - font-weight: var(--bold-on-dark); - } - } + .grid { + width: 100%; + max-width: 55rem; + display: grid; + grid-template-columns: 3rem repeat(var(--time-length), 1fr); + grid-template-rows: 2rem repeat(var(--days-length), 1fr); + border: 2px solid var(--border-500); + border-radius: 10px 10px 0 0; + + @media screen and (max-width: $device-s-width), (pointer: coarse) { + font-size: 12px; + } + + .time { + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem; + grid-row: 1; + grid-column: var(--offset) / span 2; + } + .day-name { + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem; + grid-column: 1; + grid-row: var(--line); + } + + .event { + display: flex; + flex-direction: row; + @media screen and (max-width: $device-s-width), (pointer: coarse) { + flex-direction: column; + } + gap: 0.25rem; + align-items: center; + justify-content: center; + margin: 0.25rem; + padding: 0.5rem; + grid-row: var(--line); + grid-column: var(--offset) / span var(--length); + background-color: var(--color); + border: 3px solid var(--color); + border-radius: 10px; + font-weight: var(--bold-on-dark); + text-align: center; + + &.highlight { + background-color: var(--highlight-color); + border: 3px solid var(--highlight-color); + } + &.selected { + border: 3px solid var(--border-color); + } + } + + .hline { + grid-column: 1 / 13; + grid-row: var(--line); + height: 0px; + border-bottom: 1px solid var(--border-500); + } + + .vline { + grid-column: var(--offset); + grid-row: 1 / calc(2 + var(--days-length)); + width: 0px; + border-right: 1px dashed var(--border-500); + } + } + .legend { + max-width: 55rem; + display: grid; + grid-template-columns: min-content 1fr; + border: 2px solid var(--border-500); + border-radius: 0 0 10px 10px; + border-top: none; + padding: 1rem; + gap: 0.5rem 1rem; + align-items: center; + justify-content: center; + width: 100%; + + .name { + width: 100%; + } + + .color { + display: flex; + justify-content: center; + align-items: center; + font-weight: 500; + width: 2rem; + height: 1.5rem; + background-color: var(--color); + border: 2px solid var(--border-color); + border-radius: 10px; + font-weight: var(--bold-on-dark); + } + } } .schedule-view {