diff --git a/client/css/style.css b/client/css/style.css index cd362c2..a1fb1f8 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1,12 +1,28 @@ -body { - background: #eee; +body, html { + padding: 0; + margin: 0; } -#sensors-container { +body { + background: #eee; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +button, input { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +.sensors { display: grid; grid-template-columns: 1fr 1fr; } +@media only screen and (max-width: 1200px) { + .sensors { + grid-template-columns: 1fr; + } +} + .sensor { background: #fff; border-radius: 0.5rem; @@ -23,4 +39,89 @@ body { .sensor .header .actions { margin-left: auto; +} + +.settings-modal { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + overflow: auto; + display: none; + justify-content: center; + align-items: flex-start; + background-color: rgba(0,0,0,0.2); + z-index: 5; +} + +.settings-modal.show { + display: flex; +} + +.settings-modal .inner { + background: #fff; + margin-top: 5%; + box-shadow: 0px 10px 15px -3px rgba(0,0,0,0.1); + border-radius: 0.5rem; +} + +.settings-modal .inner .body { + padding: 0.75rem 1rem; +} + +form { + display: flex; + flex-direction: column; +} + +form .input { + display: flex; + flex-direction: column; + margin-bottom: 0.5rem; +} + +form .actions { + text-align: right; +} + + +form.horizontal { + flex-direction: row; + align-items: center; +} + +form.horizontal .input { + flex-direction: row; + margin-bottom: 0; + margin-right: 0.5rem; + align-items: baseline; +} + +form.horizontal .input label { + margin-right: 0.25rem; +} + +.filters { + display: flex; + align-items: center; + padding: 0.5rem 0.5rem; + margin-bottom: 1rem; + background-color: #fff; + box-shadow: 0px 10px 15px -3px rgba(0,0,0,0.1); +} + +.filters .actions { + margin-left: auto; + display: flex; + align-items: center; +} + +.checkbox-label { + display: inline-flex; + align-items: center; +} + +.checkbox-label input[type=checkbox] { + margin-top: 6px; } \ No newline at end of file diff --git a/client/js/index.js b/client/js/index.js index b9e71a2..a2d0cc0 100644 --- a/client/js/index.js +++ b/client/js/index.js @@ -1,18 +1,222 @@ const { html, render } = lighterhtml; + const $container = document.getElementById("sensors-container"); +const $filters = document.createElement("div"); +$filters.className = "filters"; +$container.appendChild($filters); + +const $sensorsContainer = document.createElement("div"); +$sensorsContainer.className = "sensors"; +$container.appendChild($sensorsContainer); + +const $config = document.createElement("div"); +$config.className = "config"; +$container.appendChild($config); + +let sensorComponents = []; + function load() { fetch("/api/sensors") .then((r) => r.json()) .then((sensors) => { - const components = sensors.map((sensor) => createSensor(sensor)); + $sensorsContainer.innerHTML = '' - components.forEach((component) => - $container.appendChild(component.container) + sensorComponents = sensors.map((sensor) => createSensor(sensor)); + sensorComponents.forEach((component) => + $sensorsContainer.appendChild(component.container) ); }); } +function preventPropagation(e) { + e.stopPropagation(); +} + +function hideConfig() { + renderConfig({ sensor: { config: {} }, shown: false }); +} + +function showConfigOf(sensor, onSave) { + renderConfig({ sensor, onSave, shown: true }); +} + +function renderConfig({ sensor, onSave, shown }) { + const handleSave = async (e) => { + e.preventDefault(); + e.stopPropagation(); + + const data = Object.fromEntries(new FormData(e.target)); + + Promise.all( + Object.entries(data).map(([name, value]) => + fetch(`/api/sensors/${sensor.sensor}/config/${name}`, { + method: "PUT", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ value }), + }) + ) + ).then(() => onSave?.()); + + hideConfig(); + }; + + const config = sensor.config; + + render( + $config, + html` +