Prima release

dev
Antonio De Lucreziis 2 years ago
parent 250b8f6830
commit 7736ab5533

3
.gitignore vendored

@ -3,3 +3,6 @@ dist/
node_modules/ node_modules/
*.local* *.local*
.env
.env.production

@ -13,6 +13,7 @@
"dependencies": { "dependencies": {
"date-fns": "^2.29.2", "date-fns": "^2.29.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"preact": "^10.10.6", "preact": "^10.10.6",
"sass": "^1.54.8", "sass": "^1.54.8",
"vite": "^3.0.9" "vite": "^3.0.9"

@ -5,6 +5,7 @@ specifiers:
'@preact/preset-vite': ^2.3.0 '@preact/preset-vite': ^2.3.0
date-fns: ^2.29.2 date-fns: ^2.29.2
lodash: ^4.17.21 lodash: ^4.17.21
lodash-es: ^4.17.21
preact: ^10.10.6 preact: ^10.10.6
sass: ^1.54.8 sass: ^1.54.8
vite: ^3.0.9 vite: ^3.0.9
@ -12,6 +13,7 @@ specifiers:
dependencies: dependencies:
date-fns: 2.29.2 date-fns: 2.29.2
lodash: 4.17.21 lodash: 4.17.21
lodash-es: 4.17.21
preact: 10.10.6 preact: 10.10.6
sass: 1.54.8 sass: 1.54.8
vite: 3.0.9_sass@1.54.8 vite: 3.0.9_sass@1.54.8
@ -777,6 +779,10 @@ packages:
resolution: {integrity: sha512-lxpCM3HTvquGxKGzHeknB/sUjuVoUElLlfYnXZT73K8geR9jQbroGlSCFBax9/0mpGoD3kzcMLnOlGQPJJNyqQ==} resolution: {integrity: sha512-lxpCM3HTvquGxKGzHeknB/sUjuVoUElLlfYnXZT73K8geR9jQbroGlSCFBax9/0mpGoD3kzcMLnOlGQPJJNyqQ==}
dev: true dev: true
/lodash-es/4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
dev: false
/lodash/4.17.21: /lodash/4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false dev: false

@ -0,0 +1,87 @@
<svg width="1000" height="500" viewBox="0 0 1000 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="80" y="190" width="60" height="120" fill="#1E6733"/>
<rect x="160" y="50" width="150" height="60" fill="#1E6733"/>
<rect x="140" y="90" width="10" height="50" fill="#ECC333"/>
<rect x="140" y="200" width="10" height="50" fill="#ECC333"/>
<rect x="140" y="410" width="10" height="20" fill="#ECC333"/>
<rect x="140" y="350" width="10" height="50" fill="#ECC333"/>
<rect x="240" y="110" width="70" height="10" fill="#ECC333"/>
<rect x="250" y="130" width="60" height="130" fill="#1E6733"/>
<rect x="340" y="50" width="60" height="120" fill="#1E6733"/>
<rect x="340" y="190" width="60" height="120" fill="#1E6733"/>
<rect x="590" y="190" width="60" height="120" fill="#1E6733"/>
<rect x="690" y="180" width="60" height="120" fill="#1E6733"/>
<rect x="690" y="310" width="60" height="140" fill="#1E6733"/>
<rect x="690" y="50" width="60" height="120" fill="#1E6733"/>
<rect x="590" y="320" width="60" height="130" fill="#1E6733"/>
<rect x="590" y="50" width="60" height="130" fill="#1E6733"/>
<rect x="420" y="240" width="150" height="60" fill="#1E6733"/>
<rect x="340" y="320" width="60" height="130" fill="#1E6733"/>
<rect x="240" y="140" width="10" height="50" fill="#ECC333"/>
<rect x="350" y="170" width="40" height="10" fill="#ECC333"/>
<rect x="330" y="330" width="10" height="50" fill="#ECC333"/>
<rect x="160" y="200" width="80" height="60" fill="#1E6733"/>
<rect x="650" y="200" width="10" height="50" fill="#ECC333"/>
<rect x="750" y="330" width="10" height="60" fill="#ECC333"/>
<rect x="800" y="450" width="40" height="10" fill="#ECC333"/>
<rect x="850" y="450" width="30" height="10" fill="#ECC333"/>
<rect x="750" y="90" width="10" height="50" fill="#ECC333"/>
<rect x="810" y="110" width="60" height="10" fill="#ECC333"/>
<rect x="580" y="330" width="10" height="50" fill="#ECC333"/>
<rect x="580" y="60" width="10" height="50" fill="#ECC333"/>
<rect x="710" y="420" width="20" height="20" fill="#C4C4C4"/>
<rect x="710" y="390" width="20" height="20" fill="#C4C4C4"/>
<rect x="100" y="280" width="20" height="10" fill="#C4C4C4"/>
<rect x="100" y="260" width="20" height="10" fill="#C4C4C4"/>
<rect x="110" y="210" width="20" height="20" fill="#C4C4C4"/>
<rect x="350" y="430" width="40" height="10" fill="#303030"/>
<rect x="350" y="410" width="40" height="10" fill="#303030"/>
<rect x="350" y="390" width="40" height="10" fill="#303030"/>
<rect x="700" y="70" width="20" height="40" fill="#303030"/>
<rect x="700" y="120" width="20" height="40" fill="#303030"/>
<rect x="610" y="280" width="20" height="20" fill="#C4C4C4"/>
<rect x="600" y="240" width="20" height="20" fill="#C4C4C4"/>
<rect x="430" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="430" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="430" y="280" width="10" height="10" fill="#C4C4C4"/>
<rect x="445" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="445" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="460" y="280" width="40" height="10" fill="#C4C4C4"/>
<rect x="475" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="475" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="505" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="505" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="520" y="265" width="10" height="15" fill="#C4C4C4"/>
<rect x="505" y="280" width="25" height="10" fill="#C4C4C4"/>
<rect x="535" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="535" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="535" y="280" width="10" height="10" fill="#C4C4C4"/>
<rect x="445" y="280" width="10" height="10" fill="#C4C4C4"/>
<rect x="460" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="460" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="490" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="490" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="520" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="550" y="250" width="10" height="10" fill="#C4C4C4"/>
<rect x="550" y="265" width="10" height="10" fill="#C4C4C4"/>
<rect x="550" y="280" width="10" height="10" fill="#C4C4C4"/>
<rect x="620" y="210" width="20" height="20" fill="#C4C4C4"/>
<rect x="370" y="70" width="20" height="30" fill="#303030"/>
<rect x="370" y="110" width="20" height="30" fill="#303030"/>
<rect x="870" y="440" width="40" height="10" transform="rotate(-90 870 440)" fill="#303030"/>
<rect x="890" y="440" width="40" height="10" transform="rotate(-90 890 440)" fill="#303030"/>
<rect x="810" y="440" width="40" height="10" transform="rotate(-90 810 440)" fill="#303030"/>
<rect x="790" y="440" width="40" height="10" transform="rotate(-90 790 440)" fill="#303030"/>
<rect x="270" y="100" width="40" height="10" transform="rotate(-90 270 100)" fill="#303030"/>
<rect x="290" y="100" width="40" height="10" transform="rotate(-90 290 100)" fill="#303030"/>
<rect x="190" y="100" width="40" height="10" transform="rotate(-90 190 100)" fill="#303030"/>
<rect x="170" y="100" width="40" height="10" transform="rotate(-90 170 100)" fill="#303030"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M140 60V170C134.477 170 130 174.477 130 180H90C90 174.477 85.5228 170 80 170V60C85.5228 60 90 55.5228 90 50H130C130 55.5228 134.477 60 140 60Z" fill="#1E6733"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M130 320H90C90 325.523 85.5229 330 80 330V440C85.5229 440 90 444.477 90 450H130C130 444.477 134.477 440 140 440V330C134.477 330 130 325.523 130 320Z" fill="#1E6733"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M770 60C775.523 60 780 55.5228 780 50H910C910 55.5228 914.477 60 920 60V100C914.477 100 910 104.477 910 110H780C780 104.477 775.523 100 770 100V60Z" fill="#1E6733"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M770 400C775.523 400 780 395.523 780 390H910C910 395.523 914.477 400 920 400V440C914.477 440 910 444.477 910 450H780C780 444.477 775.523 440 770 440V400Z" fill="#1E6733"/>
<rect x="750" y="190" width="10" height="40" fill="#ECC333"/>
<rect x="750" y="240" width="10" height="20" fill="#ECC333"/>
<rect x="400" y="200" width="10" height="40" fill="#ECC333"/>
<rect x="400" y="250" width="10" height="20" fill="#ECC333"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

@ -1,19 +1,28 @@
import { useRef } from 'preact/hooks' import { useRef, useState } from 'preact/hooks'
import { Icon } from './Icon.jsx' import { Icon } from './Icon.jsx'
export const ComboBox = ({ selected, options }) => { let ids = 0
function generateId() {
return 'combo-id-' + ids++
}
export const ComboBox = ({ options, value, setValue }) => {
const [uid] = useState(() => generateId())
const selectRef = useRef() const selectRef = useRef()
return ( return (
<div class="input-combo" onClick={() => selectRef.current?.click()}> <div class="input-combo">
<select ref={selectRef}> <select ref={selectRef} id={uid} onInput={e => setValue(e.target.value)}>
{options.map(({ label, value }) => ( {options.map(option => (
<option value={value} selected={value === selected}> <option value={option.value} selected={option.value === value}>
{label} {option.label}
</option> </option>
))} ))}
</select> </select>
<label class="icon" for={uid}>
<Icon name="expand_more" /> <Icon name="expand_more" />
</label>
</div> </div>
) )
} }

@ -0,0 +1,16 @@
import { withClasses } from '../utils.jsx'
export const CompoundButton = ({ options, value, setValue }) => {
return (
<div class="compound">
{options.map(option => (
<button
class={withClasses(['radio', option.value === value && 'selected'])}
onClick={() => setValue(option.value)}
>
{option.label}
</button>
))}
</div>
)
}

@ -1,7 +1,7 @@
import { ComboBox } from './ComboBox.jsx' import { ComboBox } from './ComboBox.jsx'
import { Icon } from './Icon.jsx' import { Icon } from './Icon.jsx'
export const HamburgerMenu = ({ onClose }) => { export const HamburgerMenu = ({ onClose, mode, setMode, source, setSource }) => {
return ( return (
<div class="menu"> <div class="menu">
<div class="header"> <div class="header">
@ -11,12 +11,29 @@ export const HamburgerMenu = ({ onClose }) => {
<div class="item logo">PHC / Orari</div> <div class="item logo">PHC / Orari</div>
</div> </div>
<div class="options"> <div class="options">
<div class="label">Gruppo Corsi</div>
<ComboBox <ComboBox
selected={1} value={source}
setValue={setSource}
options={[ options={[
{ label: 'Prova 1', value: 1 }, { value: 'anno-1', label: 'I' },
{ label: 'Prova 2', value: 2 }, { value: 'anno-2', label: 'II' },
{ label: 'Prova 3', value: 3 }, { value: 'anno-3', label: 'III' },
{ value: 'magistrale', label: 'Magistrale' },
{ value: 'tutti', label: 'Tutti' },
]}
/>
<div class="label">Visualizzazione</div>
<ComboBox
value={mode}
setValue={setMode}
options={[
{ value: 'course', label: 'Corsi' },
{ value: 'work-week-v', label: 'Settimana' },
{
value: 'work-week-h',
label: 'Settimana (Trasposto)',
},
]} ]}
/> />
</div> </div>

@ -1 +1,5 @@
export const Icon = ({ name }) => <span class="material-symbols-outlined">{name}</span> export const Icon = ({ name, ...rest }) => (
<span class="material-symbols-outlined" {...rest}>
{name}
</span>
)

@ -0,0 +1,17 @@
import { Icon } from './Icon.jsx'
export const Popup = ({ title, children, onClose }) => {
return (
<div class="popup-container">
<div class="popup">
<div class="header">
<div class="title">{title}</div>
<button class="flat icon" onClick={onClose}>
<Icon name="close" />
</button>
</div>
<div class="content">{children}</div>
</div>
</div>
)
}

@ -1,6 +1,7 @@
import { CompoundButton } from './CompoundButton.jsx'
import { Icon } from './Icon.jsx' import { Icon } from './Icon.jsx'
export const Toolbar = ({ mode, setMode, source, setSource, onShowMenu }) => { export const Toolbar = ({ mode, setMode, source, setSource, onShowMenu, onHelp }) => {
return ( return (
<div class="toolbar"> <div class="toolbar">
<div class="mobile"> <div class="mobile">
@ -8,72 +9,54 @@ export const Toolbar = ({ mode, setMode, source, setSource, onShowMenu }) => {
<Icon name="menu" /> <Icon name="menu" />
</button> </button>
</div> </div>
<div class="item logo">PHC / Orari</div> <div class="item logo">
<img src="logo-circuit-board.svg" alt="logo" /> / <span>Orari</span>
</div>
<div class="option-group"> <div class="option-group">
<div class="item option"> <div class="item option">
<div class="compound"> <CompoundButton
<button options={[
class={'radio' + (source === 'anno-1' ? ' selected' : '')} { value: 'anno-1', label: 'I' },
onClick={() => setSource('anno-1')} { value: 'anno-2', label: 'II' },
> { value: 'anno-3', label: 'III' },
I { value: 'magistrale', label: 'Magistrale' },
</button> { value: 'tutti', label: 'Tutti' },
<button ]}
class={'radio' + (source === 'anno-2' ? ' selected' : '')} value={source}
onClick={() => setSource('anno-2')} setValue={setSource}
> />
II
</button>
<button
class={'radio' + (source === 'anno-3' ? ' selected' : '')}
onClick={() => setSource('anno-3')}
>
III
</button>
<button
class={'radio' + (source === 'magistrale' ? ' selected' : '')}
onClick={() => setSource('magistrale')}
>
Magistrale
</button>
{/* TODO: Aggiungere il pulsante per "tutti" */}
{/* <button
class={'radio' + (source === 'tutti' ? ' selected' : '')}
onClick={() => setSource('tutti')}
>
Tutti
</button> */}
</div> </div>
</div> </div>
<div class="option-group">
<div class="item option"> <div class="item option">
<div class="compound"> <CompoundButton
<button options={[
class={'radio' + (mode === 'course' ? ' selected' : '')} { value: 'course', label: 'Corsi' },
onClick={() => setMode('course')} { value: 'work-week-v', label: 'Settimana' },
> {
Corso value: 'work-week-h',
</button> label: (
<button <>
class={'radio' + (mode === 'work-week-v' ? ' selected' : '')} <Icon name="warning" />
onClick={() => setMode('work-week-v')}
>
Settimana
</button>
<button
class={'radio' + (mode === 'work-week-h' ? ' selected' : '')}
onClick={() => setMode('work-week-h')}
>
Settimana<sup>T</sup> Settimana<sup>T</sup>
</button> </>
</div> ),
},
]}
value={mode}
setValue={setMode}
/>
</div> </div>
</div>
<div class="option-group">
<div class="item option"> <div class="item option">
<button class="icon"> <button class="icon" onClick={() => window.print()}>
<Icon name="print" /> <Icon name="print" />
</button> </button>
</div> </div>
<div class="item option">
<button class="icon" onClick={onHelp}>
<Icon name="question_mark" />
</button>
</div>
</div> </div>
</div> </div>
) )

@ -5,32 +5,31 @@ import { ToolOverlay } from './components/CourseVisibility.jsx'
import { EventsView } from './components/EventsView.jsx' import { EventsView } from './components/EventsView.jsx'
import { HamburgerMenu } from './components/HamburgerMenu.jsx' import { HamburgerMenu } from './components/HamburgerMenu.jsx'
import { Icon } from './components/Icon.jsx'
import { Popup } from './components/Popup.jsx'
import { Toolbar } from './components/Toolbar.jsx' import { Toolbar } from './components/Toolbar.jsx'
window._ = _ // window._ = _
window.dataBuffer = {} // window.dataBuffer = {}
const CALENDAR_IDS = { const CALENDAR_IDS = {
'anno-1': '6308cfcb1df5cb026699ce32', 'anno-1': ['6308cfcb1df5cb026699ce32'],
'anno-2': '6308e2dc09352a0208fefdd9', 'anno-2': ['6308e2dc09352a0208fefdd9'],
'anno-3': '6308e42a1df5cb026699ced4', 'anno-3': ['6308e42a1df5cb026699ced4'],
'magistrale': '6308e8ea0c34e703bb1f7e85', 'magistrale': ['6308e8ea0c34e703bb1f7e85'],
'tutti': [
'6308cfcb1df5cb026699ce32',
'6308e2dc09352a0208fefdd9',
'6308e42a1df5cb026699ced4',
'6308e8ea0c34e703bb1f7e85',
],
} }
const App = ({}) => { async function loadEventi(ids) {
const [source, setSource] = useState('magistrale') const calendari = await Promise.all(
ids.map(async id => {
const [eventi, setEventi] = useState([]) // Almost directly copy-pasted from Chrome Dev Tools
const req = await fetch(
const [selectedCourses, setSelectedCourses] = useState([])
useEffect(() => {
setSelectedCourses([])
}, [source])
useEffect(() => {
// Directly copy-pasted from chrome Dev Tools
const request = fetch(
'https://apache.prod.up.cineca.it/api/Impegni/getImpegniCalendarioPubblico', 'https://apache.prod.up.cineca.it/api/Impegni/getImpegniCalendarioPubblico',
{ {
headers: { headers: {
@ -42,7 +41,7 @@ const App = ({}) => {
body: JSON.stringify({ body: JSON.stringify({
mostraImpegniAnnullati: true, mostraImpegniAnnullati: true,
mostraIndisponibilitaTotali: false, mostraIndisponibilitaTotali: false,
linkCalendarioId: CALENDAR_IDS[source], linkCalendarioId: id,
clienteId: '628de8b9b63679f193b87046', clienteId: '628de8b9b63679f193b87046',
pianificazioneTemplate: false, pianificazioneTemplate: false,
dataInizio: '2022-10-02T22:00:00.000Z', dataInizio: '2022-10-02T22:00:00.000Z',
@ -54,19 +53,36 @@ const App = ({}) => {
} }
) )
// const request = fetch('/data.json') return await req.json()
})
)
console.time('loading eventi') console.log(calendari)
request if (ids.length === 1) {
.then(res => res.json()) return calendari[0]
.then(data => { }
console.timeEnd('loading eventi')
window.dataBuffer[source] = data return _.uniqBy(_.concat(...calendari), 'id')
setEventi(data) }
})
const App = ({}) => {
const [source, setSource] = useState('magistrale')
const [eventi, setEventi] = useState([])
const [selectedCourses, setSelectedCourses] = useState([])
useEffect(() => {
setSelectedCourses([])
}, [source])
useEffect(async () => {
const eventi = await loadEventi(CALENDAR_IDS[source])
// window.dataBuffer[source] = eventi
setEventi(eventi)
}, [source]) }, [source])
const [helpVisible, setHelpVisible] = useState(false)
const [mode, setMode] = useState('course') const [mode, setMode] = useState('course')
const [hideOtherCourses, setHideOtherCourses] = useState(false) const [hideOtherCourses, setHideOtherCourses] = useState(false)
@ -76,7 +92,7 @@ const App = ({}) => {
setHideOtherCourses(false) setHideOtherCourses(false)
} }
const [showMobileMenu, setShowMobileMenu] = useState(true) const [showMobileMenu, setShowMobileMenu] = useState(false)
return ( return (
<> <>
@ -87,6 +103,7 @@ const App = ({}) => {
source, source,
setSource, setSource,
onShowMenu: () => setShowMobileMenu(true), onShowMenu: () => setShowMobileMenu(true),
onHelp: () => setHelpVisible(true),
}} }}
/> />
<EventsView <EventsView
@ -120,11 +137,93 @@ const App = ({}) => {
)} )}
{showMobileMenu && ( {showMobileMenu && (
<HamburgerMenu <HamburgerMenu
onClose={() => { {...{
mode,
setMode,
source,
setSource,
onClose: () => {
setShowMobileMenu(false) setShowMobileMenu(false)
},
}} }}
/> />
)} )}
{helpVisible && (
<Popup
title={
<>
<Icon name="info" /> Aiuto
</>
}
onClose={() => setHelpVisible(false)}
>
<h1>Titolo</h1>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<h2>Sottotitolo</h2>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<h2>Sottotitolo</h2>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<h3>Sottosottotitolo</h3>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<h3>Sottosottotitolo</h3>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Similique eum
pariatur amet. Laborum quos repudiandae corrupti, exercitationem cum labore
doloribus.
</p>
</Popup>
)}
</> </>
) )
} }

@ -38,7 +38,7 @@ html {
--accent-900: #244624; --accent-900: #244624;
} }
$device-s-width: 480px; $device-s-width: 420px;
// Elements // Elements
@ -51,12 +51,43 @@ sup {
transform: translate(0, -0.25rem); transform: translate(0, -0.25rem);
} }
// Headings
$base-font-size: 18px;
$heading-scale: 1.25;
@function pow($number, $exponent) {
$value: 1;
@if $exponent > 0 {
@for $i from 1 through $exponent {
$value: $value * $number;
}
}
@return $value;
}
@for $i from 1 through 4 {
h#{$i} {
margin: 0;
$factor: pow($heading-scale, 4 - $i);
font-size: $base-font-size * $factor;
font-weight: 500;
line-height: 1.5;
}
}
button, button,
.button { .button {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 0.25rem;
background: var(--gray-000); background: var(--gray-000);
border: 2px solid var(--gray-350); border: 2px solid var(--gray-350);
border-radius: 0.5rem; border-radius: 0.5rem;
@ -94,11 +125,15 @@ button,
&.icon { &.icon {
padding: 0.5rem; padding: 0.5rem;
} }
&.flat {
border: none;
}
} }
.input-combo { .input-combo {
display: flex; display: flex;
align-items: center; // align-items: center;
justify-content: center; justify-content: center;
height: 3rem; height: 3rem;
@ -109,15 +144,13 @@ button,
font-weight: 400; font-weight: 400;
padding: 0.5rem 0.5rem 0.5rem 1rem; // padding: 0.5rem 0.5rem 0.5rem 1rem;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
gap: 0.5rem;
select { select {
border: none; border: none;
background: none; background: none;
@ -125,14 +158,92 @@ button,
font-family: inherit; font-family: inherit;
font-size: inherit; font-size: inherit;
font-weight: 300;
height: 100%;
padding-left: 1rem;
padding-right: 3rem;
width: 100%;
}
.icon {
position: absolute;
right: 0;
height: 100%;
width: calc(3rem - 4px);
display: grid;
place-content: center;
pointer-events: none;
} }
} }
// Components // Components
.popup-container {
position: absolute;
inset: 0;
z-index: 10;
background: #00000099;
max-width: 100%;
.popup {
@extend .panel;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
gap: 1rem;
box-shadow: 0 0 1.5rem #00000033;
.header {
display: flex;
align-items: center;
justify-content: space-between;
user-select: none;
.title {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 24px;
}
}
.content {
@extend .text-block;
width: 60ch;
max-width: 100%;
max-height: 60vh;
overflow-y: auto;
}
max-width: 100%;
}
}
.compound { .compound {
display: flex; display: flex;
.material-symbols-outlined {
font-size: 20px;
}
button, button,
.button { .button {
border-radius: 0; border-radius: 0;
@ -175,6 +286,8 @@ button,
} }
} }
// Extension Classes
.panel { .panel {
background: var(--gray-000); background: var(--gray-000);
border: 1px solid var(--gray-500); border: 1px solid var(--gray-500);
@ -182,6 +295,12 @@ button,
padding: 1rem; padding: 1rem;
} }
.text-block {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
// Structure // Structure
body { body {
@ -190,6 +309,8 @@ body {
font-weight: 300; font-weight: 300;
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
position: relative;
} }
.toolbar { .toolbar {
@ -213,10 +334,22 @@ body {
gap: 1rem; gap: 1rem;
@media screen and (max-width: $device-s-width) { @media screen and (max-width: $device-s-width) and (orientation: portrait),
screen and (max-height: $device-s-width) and (orientation: landscape) {
display: none; display: none;
} }
} }
.logo {
display: flex;
align-items: center;
img {
max-height: 2.5rem;
}
gap: 0.25rem;
}
} }
.events-view { .events-view {
@ -254,6 +387,8 @@ body {
position: relative; position: relative;
cursor: pointer;
&.selected { &.selected {
background: var(--gray-100); background: var(--gray-100);
@ -308,6 +443,10 @@ body {
.day { .day {
box-sizing: content-box; box-sizing: content-box;
// @media screen and (max-width: $device-s-width) {
// writing-mode: vertical-lr;
// }
height: calc(var(--size) * var(--event-height) + (var(--size) - 1) * 0.25rem); height: calc(var(--size) * var(--event-height) + (var(--size) - 1) * 0.25rem);
border-top: 1px solid var(--gray-500); border-top: 1px solid var(--gray-500);
@ -452,7 +591,7 @@ body {
overflow: scroll; overflow: scroll;
--event-height: 4.25rem; --event-height: 4.75rem;
.pivot { .pivot {
height: 3rem; height: 3rem;
@ -507,7 +646,8 @@ body {
border-top: 1px solid var(--gray-500); border-top: 1px solid var(--gray-500);
@media screen and (max-width: $device-s-width) { @media screen and (max-width: $device-s-width) and (orientation: portrait),
screen and (max-height: $device-s-width) and (orientation: landscape) {
writing-mode: vertical-lr; writing-mode: vertical-lr;
} }
@ -658,7 +798,9 @@ body {
} }
// not on mobile // not on mobile
@media screen and (min-width: $device-s-width) {
@media screen and (min-width: $device-s-width) and (orientation: portrait),
screen and (min-height: $device-s-width) and (orientation: landscape) {
.mobile { .mobile {
display: none; display: none;
} }
@ -669,20 +811,19 @@ body {
} }
// on mobile // on mobile
@media screen and (max-width: $device-s-width) {
@media screen and (max-width: $device-s-width) and (orientation: portrait),
screen and (max-height: $device-s-width) and (orientation: landscape) {
.toolbar { .toolbar {
padding: 0.75rem 1rem 0.75rem 0.75rem; padding: 0.75rem 1rem 0.75rem 0.75rem;
} }
button {
&.flat {
border: none;
}
}
.menu { .menu {
position: absolute; position: absolute;
inset: 0; top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: var(--gray-000); background: var(--gray-000);
@ -703,10 +844,33 @@ body {
.options { .options {
padding: 1rem; padding: 1rem;
display: flex; display: grid;
flex-direction: column; grid-template-columns: auto 1fr;
gap: 1rem;
align-items: center; align-items: center;
.label {
font-weight: 400;
}
}
}
} }
@media print {
// @page {
// size: A4 landscape;
// }
.toolbar,
.menu,
.overlay {
display: none;
}
.work-week-v-view {
overflow: visible;
} }
} }

@ -0,0 +1,7 @@
export const withClasses = classes =>
Array.isArray(classes)
? classes.filter(e => !!e).join(' ')
: Object.entries(classes)
.filter(([, value]) => value)
.map(([key]) => key)
.join(' ')

@ -1,10 +1,21 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import preact from '@preact/preset-vite' import preact from '@preact/preset-vite'
import { loadEnv } from 'vite'
export default defineConfig({ export default defineConfig(({ mode }) => {
console.log(`Build Mode: "${mode}"`)
const env = loadEnv(mode, process.cwd(), '')
return {
base: mode === 'development' ? '/' : env.BASE_URL,
server: { server: {
port: 3000, port: 3000,
}, },
plugins: [preact()], plugins: [preact()],
esbuild: {
logOverride: { 'this-is-undefined-in-esm': 'silent' },
},
}
}) })

Loading…
Cancel
Save