
Ahoj Tomáši, jak už jsem psal, nemyslím si, že je potřeba mít tuto funkcionalitu offline. Mám pro to následující důvody: - Beru to tak, že události zaznamenávané do kalendáře nejsou kritické (nejde o prachy), takže pokud to tu a tam nebude fungovat, nic moc se nestane. - Offline režim je schválně dělán jako minimalistický a toto z něj dělá režim maximalistický (viz níže). - Bylo by potřeba dodělat do buildrootu (terminal/src/buildroot/package/merica/mt-apps/mt-apps.mk) instalaci všech souborů v adresáři html. - Tím by se na terminál nainstalovalo 29 MB šrotu, což docela hodně (asi 500x víc, než je současná velikost všech zdrojáků). Při instalaci jen dist/ souborů je to jen 6 MB, ale i to je docela dost. - Při jakékoli změně v těchto souborech (a proto, že to je tolik dat, tak i těch změn určitě nebude málo) by se musela updatovat data na SD kartě terminálu. Ne, že by to nešlo, ale na terminálu je jen minimální instalace Linuxu, takže je to zbytečný "vopruz". - V současném kódu je off-line and on-line funkcionalita dost propletena a je to hodně nepřehledné. Myslím, že kalendář by mohl být klidně načten ze serveru a pokud server na nějakou dobu vypadne, mohl by fungovat i chvíli bez něj. Ale není potřeba, aby byl kalendář k dispozici hned po naběhnutí terminálu i bez serveru. Další komentáře posílám v kódu níže. On Fri, Aug 31 2018, Prochazka, Tomas wrote:
This commit implementing function for add events as 'Milk clean' to calendar. Calendar is running offline, so not need any data for create calendar from server.
Every user sees events, which created another users (this events are displayed 'red' color ), but only user which created events(other color) he can edit it(delete). If user wants to create event, he must be logged in. After logging in, it will be displayed image with calendar. --- .gitmodules | 15 +++ html/bower_components/bootstrap | 1 + html/bower_components/bootstrap3-dialog | 1 + html/bower_components/fullcalendar | 1 + html/bower_components/jquery | 1 + html/bower_components/moment | 1 + html/calendar.html | 211 ++++++++++++++++++++++++++++++++ html/calendar.svg | 51 ++++++++ html/index.html | 37 +++++- 9 files changed, 317 insertions(+), 2 deletions(-) create mode 160000 html/bower_components/bootstrap create mode 160000 html/bower_components/bootstrap3-dialog create mode 160000 html/bower_components/fullcalendar create mode 160000 html/bower_components/jquery create mode 160000 html/bower_components/moment create mode 100644 html/calendar.html create mode 100644 html/calendar.svg
diff --git a/.gitmodules b/.gitmodules index 56c12ef..7850202 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,18 @@ [submodule "libwebsockets"] path = libwebsockets url = https://github.com/warmcat/libwebsockets.git +[submodule "html/bower_components/jquery"] + path = html/bower_components/jquery + url = https://github.com/jquery/jquery-dist.git +[submodule "html/bower_components/bootstrap"] + path = html/bower_components/bootstrap + url = https://github.com/twbs/bootstrap.git +[submodule "html/bower_components/bootstrap3-dialog"] + path = html/bower_components/bootstrap3-dialog + url = https://github.com/nakupanda/bootstrap3-dialog.git +[submodule "html/bower_components/moment"] + path = html/bower_components/moment + url = https://github.com/moment/moment +[submodule "html/bower_components/fullcalendar"] + path = html/bower_components/fullcalendar + url = https://github.com/fullcalendar/fullcalendar diff --git a/html/bower_components/bootstrap b/html/bower_components/bootstrap new file mode 160000 index 0000000..0b9c4a4 --- /dev/null +++ b/html/bower_components/bootstrap @@ -0,0 +1 @@ +Subproject commit 0b9c4a4007c44201dce9a6cc1a38407005c26c86 diff --git a/html/bower_components/bootstrap3-dialog b/html/bower_components/bootstrap3-dialog new file mode 160000 index 0000000..3dd11d5 --- /dev/null +++ b/html/bower_components/bootstrap3-dialog @@ -0,0 +1 @@ +Subproject commit 3dd11d586f78de75356af418907ec6e3b347377c diff --git a/html/bower_components/fullcalendar b/html/bower_components/fullcalendar new file mode 160000 index 0000000..3ddb3c8 --- /dev/null +++ b/html/bower_components/fullcalendar @@ -0,0 +1 @@ +Subproject commit 3ddb3c8d1461c6b841aed3487ba07d8ba338adb6 diff --git a/html/bower_components/jquery b/html/bower_components/jquery new file mode 160000 index 0000000..9e8ec3d --- /dev/null +++ b/html/bower_components/jquery @@ -0,0 +1 @@ +Subproject commit 9e8ec3d10fad04748176144f108d7355662ae75e diff --git a/html/bower_components/moment b/html/bower_components/moment new file mode 160000 index 0000000..db71a65 --- /dev/null +++ b/html/bower_components/moment @@ -0,0 +1 @@ +Subproject commit db71a655fc51fe58009675608a400d0d4cd0ca87 diff --git a/html/calendar.html b/html/calendar.html new file mode 100644 index 0000000..5c21b19 --- /dev/null +++ b/html/calendar.html @@ -0,0 +1,211 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset='utf-8' /> + <link href='bower_components/fullcalendar/dist/fullcalendar.min.css' rel='stylesheet' /> + <link href='bower_components/fullcalendar/dist/fullcalendar.print.min.css' rel='stylesheet' media='print' /> + <script src='bower_components/moment/min/moment.min.js'></script> + <script src='bower_components/jquery/dist/jquery.min.js'></script> + <script src='bower_components/fullcalendar/dist/fullcalendar.min.js'></script> + <script> + var color_event = '#FF0000'; //color for events, which user can not edit
Lepší by bylo pojmenovat tu proměnnou event_color_ro (jako read only). Ale nevím, jestli je dobré ji definovat jako globální proměnnou - může se tlouct o jméno s jinými skripty (čert ví, co v těch 29 MB všecho je :-). Nebyla by lepší definice v css?
+ var Object_services = [{ + id: "clean_milk", + name: "Clean milk",
Milk container cleaned
+ color: '#178006' + }, { + id: "open_new_box", + name: "Open new box",
New coffee opened
+ color: '#062e80' + }, { + id: "last_coffee_box", + name: "Last coffee box",
Tohle má znamenat co? Že někdo načal poslední pytel kafe? Není jasné, jestli se v takovém případě má zaškrtnout i předchozí možnost nebo ne. Viz také níže.
+ color: '#862e80' + }];
Chybí tu Např. čištění celého kávovaru (ne jen piksly na mléko).
+ + function settingRightVariables() { + document.getElementById("id1").id = String(Object_services[0].id); + document.getElementById("id1_label").textContent = String(Object_services[0].name); + document.getElementById("id2").id = String(Object_services[1].id); + document.getElementById("id2_label").textContent = String(Object_services[1].name); + document.getElementById("id3").id = String(Object_services[2].id); + document.getElementById("id3_label").textContent = String(Object_services[2].name); + }
Proč to nastavuješ pomocí skriptu? Když ty id a textContent nastavíš rovnou v html dole, tak to bude kratší a přehlednější.
+ var calendar; + $(document).ready(function() { + calendar = $('#calendar').fullCalendar({ + header: { + left: 'prev,next today', + center: 'title', + right: '' + }, + selectable: true, + selectHelper: true, + selectConstraint: { + start: '00:00', + end: '24:00' + }, + select: function(start, end) { + var array = $('#calendar').fullCalendar('clientEvents', function(events) { + return (moment(events.start).format('YYYY-MM-DD') >= moment(start).format('YYYY-MM-DD') && moment(end).format('YYYY-MM-DD') > moment(events.start).format('YYYY-MM-DD'))
Tohle rozděl na víc řádků, ať se to dá pochopit i bez scrolování. A není mi jasné to porovnání endu ze startem. Nevybereš tím všechny události v budoucnosti?
+ }); + var date_start = moment(start).format('YYYY-MM-DD'); + var date_end = moment(end).format('YYYY-MM-DD');
Když tyhle proměnné nadefinuješ před array, tak ten kód funkce bude kratší a přehlednější.
+ var i; + for (i = 0; i < Object_services.length; i++) { + var checkBox = document.getElementById(String(Object_services[i].id)); + var foundValue = array.filter(obj => obj.title === Object_services[i].name); + var title; + if (checkBox.checked == true) { + var title = String(Object_services[i].name); + if (foundValue.length == 0) { + var eventData; + eventData = { + title: title, + start: start, + color: Object_services[i].color + }; + $('#calendar').fullCalendar('renderEvent', eventData, true); // stick? = true + let queue = JSON.parse(localStorage.getItem("events_local")) || []; + var id = localStorage.getItem("id_local"); + queue.push({ + data: JSON.stringify({ + type: "events", + time: date_start, + title: title, + id: id, + delete_events: false + }) + }); + localStorage.setItem("events_local", JSON.stringify(queue)); + } + } + }
Tohle je dost divoké. Mám následující připomínky: 1) Přidávání událostí není moc intuitivní. Člověk neví, jestli má nejdřív kliknout do kalendáře a pak vybrat "Service", nebo naopak. Chtělo by to napsat uživateli, co a jak má dělat. Ale... 2) Přidávání událostí pomocí kalendáře je zbytečné či možná dokonce nežádoucí. Když vyčistím kávovar, tak chci kliknout, že jsem vyčistil kávovar a nechci klikat na datum, protože budu vždy klikat na dnešek. Čili, kalendář bych použil jen na zobrazování událostí, přidávání bych dělal jen stiskem jednoho tlačítka.
+ $('#calendar').fullCalendar('unselect'); + }, + eventClick: function(event) { + if (event.editable) { + var answer = confirm("Are you want remove this events (" + event.title + ") from " + moment(event.start).format('DD-MM-YYYY') + " ?")
Mazání nějak blbnw. Když si naklikám víc událostí v různých dnes a pak je zkouším smazat, tak to občas nefunguje - ani se nezobrazí tato otázka.
+ if (answer) { + $('#calendar').fullCalendar('removeEvents', event._id); + var id = localStorage.getItem("id_local"); + let queue = JSON.parse(localStorage.getItem("events_local")) || []; + queue.push({ + data: JSON.stringify({ + type: "events", + time: moment(event.start).format('YYYY-MM-DD'), + title: event.title, + id: id, + delete_events: true + }) + });
Tohle je taky nějaký divoký. Když chceš smazat událost, tak ji tam přidáš ještě jednou, ale nastavíš flag na true a odstraníš jí při druhém průchodu? Nemůžeš jí odstranit rovnou bez toho flagu delete_events?
+ let queue2 = []; + if (Array.isArray(queue)) { + for (var i = queue.length - 1; i >= 0; i--) {
Proč se to prochází pozpátku? Bude pak dobře fungovat mazání zprávy ze serveru, když smažu
+ let entry = queue[i]; + let entry2 = JSON.parse(entry.data); + if (!((moment(event.start).format('YYYY-MM-DD') == entry2.time) && (event.title == entry2.title))) { + queue2.push(entry); + } else { + entry2.delete_events = true; + queue2.push({ + data: JSON.stringify(entry2) //if server offline, we must event remove from localStorage + }); + } + } + localStorage.removeItem("events_local"); + localStorage.setItem("events_local", JSON.stringify(queue2)); + }
Aha, už to chápu, to je asi kvůli tomu, že to potřebuješ odstranit i ze serveru. Ale stejně je to divoký. Kdyby se třeba časem přidávala událost, která může nastat víckrát za den, tak se to bude muset celé předělat. Další důvod pro to, nemít kalendář offline. Takhle se bojím, že v té synchronizaci local storage a serveru budou chyby.
+ } + } + }, + //editable: false, + eventLimit: true + }); + + let queue = JSON.parse(localStorage.getItem("events_server")) || []; //event from server
Kdo nastavuje events_server? Nikde jinde to nevidím. Pokud to jsou události, které pošle flask, tak není důvod na to používat localStorage. Kód pro přidávání událostí ze serveru může být celý na serveru.
+ localStorage.removeItem("events_server"); + var id = localStorage.getItem("id_local"); //how user is login + if (Array.isArray(queue)) { + if (queue.length) { + for (var i = queue.length - 1; i >= 0; i--) {
Proč iteruješ pozpátku?
+ let entry = queue[i]; + addEventstoCalendar(id, entry); + } + } else { + let queue = JSON.parse(localStorage.getItem("events_local")) || []; //event from local + if (Array.isArray(queue)) { + for (var i = queue.length - 1; i >= 0; i--) { + let entry = queue[i]; + entry = JSON.parse(entry.data); + if (!entry.delete_events) { + addEventstoCalendar(id, entry); + } + } + } + } + } + + }); + + function addEventstoCalendar(id, entry) { + var color = color_event; + var eventData = { + title: entry.title, + start: entry.time, + color: color, + editable: false + }; + if (id == entry.id) { + for (var j = 0; j < Object_services.length; j++) { + if (entry.title == String(Object_services[j].name)) { + eventData.color = Object_services[j].color; + break; + } + } + eventData.editable = true; + } + calendar.fullCalendar('renderEvent', eventData, true); + } + </script> + <style> + body { + margin: 20px 10px; + padding: 0; + font-family: "Lucida Grande", Helvetica, Arial, Verdana, sans-serif; + font-size: 14px; + } + #calendar { + max-width: 800px; + margin: 0 auto; + } + fieldset { + font-size: 15px; + padding: 10px; + width: 450px; + margin: 0 auto; + text-align: center; + line-height: 1.8; + } + </style> + +</head> + +<body> + <div id='calendar'></div> + <script> + settingRightVariables(); + </script> + <fieldset style="text-align:center"> + <legend align="center"> Services</legend> + <input style="vertical-align:middle" type="checkbox" id="id1"> + <label style="vertical-align:middle" id="id1_label">A</label> + <input style="vertical-align:middle" type="checkbox" id="id2"> + <label style="vertical-align:middle" id="id2_label">B</label> + <input style="vertical-align:middle" type="checkbox" id="id3"> + <label style="vertical-align:middle" id="id3_label">C</label> + </fieldset> +</body> + +</html> diff --git a/html/calendar.svg b/html/calendar.svg new file mode 100644 index 0000000..4d781eb --- /dev/null +++ b/html/calendar.svg @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#77B3D4;} + .st1{opacity:0.2;} + .st2{fill:#231F20;} + .st3{fill:#FFFFFF;} + .st4{fill:#C75C5C;} + .st5{fill:#4F5D73;} + .st6{fill:#E0E0D1;} +</style> +<g id="Layer_1"> + <g> + <circle class="st0" cx="32" cy="32" r="32"/> + </g> + <g> + <g class="st1"> + <path class="st2" d="M12,25v25c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V25H12z"/> + </g> + <g> + <path class="st3" d="M12,23v25c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V23H12z"/> + </g> + <g class="st1"> + <path class="st2" d="M48,14H16c-2.2,0-4,1.8-4,4v7h40v-7C52,15.8,50.2,14,48,14z"/> + </g> + <g> + <path class="st4" d="M48,12H16c-2.2,0-4,1.8-4,4v7h40v-7C52,13.8,50.2,12,48,12z"/> + </g> + <g> + <path class="st5" d="M32,48c-1.1,0-2-0.9-2-2c0-5.5,1.8-9.5,3.5-12H27c-1.1,0-2-0.9-2-2s0.9-2,2-2h11c0.9,0,1.6,0.6,1.9,1.4 + s0,1.7-0.7,2.2C39,33.8,34,37.5,34,46C34,47.1,33.1,48,32,48z"/> + </g> + <g class="st1"> + <path class="st2" d="M20,21c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C22,20.1,21.1,21,20,21L20,21z"/> + </g> + <g class="st1"> + <path class="st2" d="M45,21c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C47,20.1,46.1,21,45,21L45,21z"/> + </g> + <g> + <path class="st6" d="M20,19c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C22,18.1,21.1,19,20,19L20,19z"/> + </g> + <g> + <path class="st6" d="M45,19c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C47,18.1,46.1,19,45,19L45,19z"/> + </g> + </g> +</g> +<g id="Layer_2"> +</g> +</svg> diff --git a/html/index.html b/html/index.html index 06f8fbf..b7e0122 100644 --- a/html/index.html +++ b/html/index.html @@ -1,6 +1,13 @@ <!doctype html> <html> <head> + +<script src="/bower_components/jquery/dist/jquery.min.js"></script> +<link href="/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"/> +<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> +<link href="/bower_components/bootstrap3-dialog/dist/css/bootstrap-dialog.min.css" rel="stylesheet" type="text/css" /> +<script src="/bower_components/bootstrap3-dialog/dist/js/bootstrap-dialog.min.js"></script> + <meta charset="utf-8"/> <title>IID Coffee Terminal</title> <script> @@ -17,6 +24,7 @@ loadServerScript();
var socket; + var dialog;
function showOfflineQueue() { let queue = JSON.parse(localStorage.getItem("offlineQueue")) || []; @@ -51,6 +59,16 @@
socket.onmessage = function(msg) { updateJSONmsg(msg.data); + var data = JSON.parse(msg.data); + if (data.uid !== undefined) { //login + localStorage.removeItem("id_local"); + localStorage.setItem("id_local",data.uid); //save id + console.log(data.uid); + document.getElementById("myImg").style.visibility='visible'; //show Image for using Calendar + }else{ + dialog.close(); + document.getElementById("myImg").style.visibility='hidden'; + } if (typeof updateRemote === "function") { updateRemote(msg.data); } else { @@ -98,6 +116,7 @@ }
function sendReset() { + document.getElementById("myImg").style.visibility='hidden'; socket.send("reset"); console.log("reset"); } @@ -106,13 +125,25 @@ socket.send("close"); console.log("close"); } + + function show_calendar() { + if (typeof updateRemote === "function") { + replayOfflineEvents(); + listEventst(); + } + dialog=BootstrapDialog.show({ + title: 'Calendar', + message: $('<div></div>').load('calendar.html') + }); + } + </script> </head> <body> <center> <h1>IID Coffee Terminal</h1> </center> - + <img id="myImg" src="calendar.svg" width="100" height="100" align="left" onclick="show_calendar()"> <div id="remote"></div>
<div id="local"> @@ -138,6 +169,8 @@ <p>Input: <span id="inputStatus"></span>, JSON message: <span id="localJSON">no data received</span></p> </center> </div> - +<script> + document.getElementById("myImg").style.visibility='hidden'; +</script> </body> </html> -- 2.11.0