Compare commits

..

40 Commits

Author SHA1 Message Date
4e7fa44301 Add delay for writing files 2021-03-23 18:27:05 -06:00
Elijah Lucian
104d39b4c6 🐛🔨 2021-03-24 00:04:04 -07:00
Elijah Lucian
88e62bdf48 Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-23 23:40:56 -07:00
Elijah Lucian
b9c8b6c1e6 💎 2021-03-23 23:40:53 -07:00
66f86827ff Allow adjusting LED time and add time info 2021-03-23 17:30:47 -06:00
Elijah Lucian
8ca4d55045 🔧 2021-03-17 10:33:11 -06:00
E
2b495779d3 adding dank transition 2021-03-11 13:11:18 -07:00
Tanner
ce46f006d5 Add random delay to fake download 2021-03-10 22:41:37 -07:00
E
5f8aad7355 Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-10 22:33:22 -07:00
E
c975432b73 🌫🎲🎰🎇📟🎏 2021-03-10 22:33:06 -07:00
be140b4c22 Add number to fake downloaded photos 2021-03-10 22:24:18 -07:00
E
3c5855ecd3 Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-10 20:04:21 -07:00
E
1499ee5736 🌠 2021-03-10 20:04:18 -07:00
Tanner
a07d4469c0 Add screenshot of network settings 2021-03-10 20:00:23 -07:00
Tanner
5e565f84c2 Add test for capture 2021-03-10 20:00:08 -07:00
E
b2cc6f4723 Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-10 19:51:34 -07:00
E
6720816a97 🗯 2021-03-10 19:51:31 -07:00
Tanner
5c15c8fcf5 Freeze requirements 2021-03-10 19:35:13 -07:00
738e688c51 Adjust capture and add logging 2021-03-10 19:22:41 -07:00
19352bf409 Add license 2021-03-10 19:22:41 -07:00
a632034751 Change statuses to ints 2021-03-10 19:22:41 -07:00
E
fa6809b507 Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-10 19:17:35 -07:00
E
c29a64b1cc 💅 2021-03-10 19:17:23 -07:00
d1486b61df Remove a zero 2021-03-10 17:57:31 -07:00
ecb8c1f815 Detect when the system isn't connected 2021-03-10 17:57:31 -07:00
ec8128d96a Add debug mode to simulate a capture 2021-03-10 17:57:31 -07:00
E
15023b0e24 🏁 2021-03-07 22:53:30 -07:00
E
534a3fe632 Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-07 22:51:36 -07:00
E
f338e14577 🧹 2021-03-07 22:51:31 -07:00
d5cfbafbde Add status API route 2021-03-07 22:50:02 -07:00
b213f4cb18 Rename partially downloaded images 2021-03-07 22:50:02 -07:00
612f47d551 Re-enable file removal 2021-03-07 22:50:02 -07:00
E
14395bdbdd 🚀 2021-03-07 22:48:48 -07:00
E
be284b9acb v0.4.2 2021-03-07 22:00:33 -07:00
E
9fe790ed9c dank swag pop and lolz 2021-03-07 21:58:36 -07:00
E
91e26fe9fc Merge branch 'master' of https://git.tannercollin.com/tanner/3Dshock 2021-03-07 21:46:43 -07:00
E
0760c93ce4 MVP 2021-03-07 21:46:40 -07:00
b1aaffa4db Add session API routes 2021-03-07 21:43:57 -07:00
0f0f6b1f7f Ignore output directory 2021-03-07 21:43:57 -07:00
e8822a8d3a Add route to create client 2021-03-07 21:43:57 -07:00
47 changed files with 811 additions and 395 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 Tanner and Elijah
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,22 +1,25 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.cada1265.chunk.css", "main.css": "/static/css/main.895ab0f5.chunk.css",
"main.js": "/static/js/main.3f5ea67c.chunk.js", "main.js": "/static/js/main.4388b0cf.chunk.js",
"main.js.map": "/static/js/main.3f5ea67c.chunk.js.map", "main.js.map": "/static/js/main.4388b0cf.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.e7edc0d2.js", "runtime-main.js": "/static/js/runtime-main.8d7962eb.js",
"runtime-main.js.map": "/static/js/runtime-main.e7edc0d2.js.map", "runtime-main.js.map": "/static/js/runtime-main.8d7962eb.js.map",
"static/js/2.6c19819d.chunk.js": "/static/js/2.6c19819d.chunk.js", "static/css/2.015dc0ab.chunk.css": "/static/css/2.015dc0ab.chunk.css",
"static/js/2.6c19819d.chunk.js.map": "/static/js/2.6c19819d.chunk.js.map", "static/js/2.6d12aa16.chunk.js": "/static/js/2.6d12aa16.chunk.js",
"static/js/3.feab56bb.chunk.js": "/static/js/3.feab56bb.chunk.js", "static/js/2.6d12aa16.chunk.js.map": "/static/js/2.6d12aa16.chunk.js.map",
"static/js/3.feab56bb.chunk.js.map": "/static/js/3.feab56bb.chunk.js.map", "static/js/3.8e9312c3.chunk.js": "/static/js/3.8e9312c3.chunk.js",
"static/js/3.8e9312c3.chunk.js.map": "/static/js/3.8e9312c3.chunk.js.map",
"index.html": "/index.html", "index.html": "/index.html",
"static/css/main.cada1265.chunk.css.map": "/static/css/main.cada1265.chunk.css.map", "static/css/2.015dc0ab.chunk.css.map": "/static/css/2.015dc0ab.chunk.css.map",
"static/js/2.6c19819d.chunk.js.LICENSE.txt": "/static/js/2.6c19819d.chunk.js.LICENSE.txt" "static/css/main.895ab0f5.chunk.css.map": "/static/css/main.895ab0f5.chunk.css.map",
"static/js/2.6d12aa16.chunk.js.LICENSE.txt": "/static/js/2.6d12aa16.chunk.js.LICENSE.txt"
}, },
"entrypoints": [ "entrypoints": [
"static/js/runtime-main.e7edc0d2.js", "static/js/runtime-main.8d7962eb.js",
"static/js/2.6c19819d.chunk.js", "static/css/2.015dc0ab.chunk.css",
"static/css/main.cada1265.chunk.css", "static/js/2.6d12aa16.chunk.js",
"static/js/main.3f5ea67c.chunk.js" "static/css/main.895ab0f5.chunk.css",
"static/js/main.4388b0cf.chunk.js"
] ]
} }

5
client/build/favicon.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://w3.org/2000/svg" viewBox="0 0 100 100">
<text y=".9em" font-size="90">
💩
</text>
</svg>

After

Width:  |  Height:  |  Size: 110 B

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><link href="/static/css/main.cada1265.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,i,a=t[0],c=t[1],l=t[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(t);p.length;)p.shift()();return u.push.apply(u,l||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var c=r[a];0!==o[c]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(e){var t=[],r=o[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=o[e]=[t,n]}));t.push(r[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"feab56bb"}[e]+".chunk.js"}(e);var c=new Error;u=function(t){a.onerror=a.onload=null,clearTimeout(l);var r=o[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),u=t&&t.target&&t.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,r[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(t)},i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpclient=this.webpackJsonpclient||[],c=a.push.bind(a);a.push=t,a=a.slice();for(var l=0;l<a.length;l++)t(a[l]);var f=c;r()}([])</script><script src="/static/js/2.6c19819d.chunk.js"></script><script src="/static/js/main.3f5ea67c.chunk.js"></script></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="3D Shock by Tanner and Elijah enterprises"/><link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"><link rel="manifest" href="/manifest.json"/><title>3D Shock!</title><link href="/static/css/2.015dc0ab.chunk.css" rel="stylesheet"><link href="/static/css/main.895ab0f5.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,i,a=t[0],c=t[1],l=t[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(t);p.length;)p.shift()();return u.push.apply(u,l||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var c=r[a];0!==o[c]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(e){var t=[],r=o[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=o[e]=[t,n]}));t.push(r[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"8e9312c3"}[e]+".chunk.js"}(e);var c=new Error;u=function(t){a.onerror=a.onload=null,clearTimeout(l);var r=o[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),u=t&&t.target&&t.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,r[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(t)},i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpclient=this.webpackJsonpclient||[],c=a.push.bind(a);a.push=t,a=a.slice();for(var l=0;l<a.length;l++)t(a[l]);var f=c;r()}([])</script><script src="/static/js/2.6d12aa16.chunk.js"></script><script src="/static/js/main.4388b0cf.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}form{max-width:500px;-webkit-flex-direction:column;flex-direction:column}form,form label{display:-webkit-flex;display:flex;margin:auto}form label{width:100%;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}:root{--color-primary:#282c34}.App-header{background-color:#282c34;background-color:var(--color-primary);min-height:100vh;display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;font-size:calc(10px + 2vmin);color:#fff}.App-link{color:#61dafb}@-webkit-keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.error{color:red}.photo-wall{display:-webkit-flex;display:flex;-webkit-justify-content:center;justify-content:center;-webkit-flex-wrap:wrap;flex-wrap:wrap;margin:2rem}.dashboard-form{margin:auto}.ant-card{margin:1rem}.ant-card img{max-width:250px}.loading-bar-container{background:#282c34;background:var(--color-primary);width:100%;height:30px;min-height:30px}.loading-bar{transition:all .5s linear;background:#6f6;height:100%}.page-head{background:#282c34;background:var(--color-primary);color:#fff!important;margin-top:0;padding:1rem}.session-toolbar button{margin:0 .5rem;padding:.5rem 1rem;height:100%;font-weight:700;text-transform:uppercase;letter-spacing:.05rem}.ant-modal-content{border-radius:1rem;padding:0!important}.ant-modal-body img{border-radius:.5rem}.ant-modal-close{font-weight:700}.ant-card{border:1px solid #282c34;border:1px solid var(--color-primary);border-radius:.2rem}.ant-card-head{background:#282c34;background:var(--color-primary);color:#fff;font-weight:700;text-transform:uppercase;letter-spacing:.2rem}.session-toolbar{-webkit-justify-content:center;justify-content:center;width:60%;margin:1rem auto}.session-toolbar h3{font-weight:700;color:#fff;margin-right:1rem}.slider{width:400px;margin:auto 1rem}.toolbar{padding:.5rem;background:#282c34;background:var(--color-primary)}.client-info{-webkit-justify-content:center;justify-content:center;width:60%;margin:1rem auto}.client-info span{margin:auto 1rem}
/*# sourceMappingURL=main.895ab0f5.chunk.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["webpack://src/index.css","webpack://src/App.css"],"names":[],"mappings":"AAAA,KACE,QAAS,CACT,mJAEY,CACZ,kCAAmC,CACnC,iCACF,CAEA,KACE,yEAEF,CAEA,KAEE,eAAgB,CAEhB,6BAAsB,CAAtB,qBACF,CAEA,gBANE,oBAAa,CAAb,YAAa,CAEb,WAUF,CANA,WACE,UAAW,CAEX,0BAAmB,CAAnB,kBAAmB,CACnB,qCAA8B,CAA9B,6BAEF,CC3BA,MACE,uBACF,CAEA,YACE,wBAAsC,CAAtC,qCAAsC,CACtC,gBAAiB,CACjB,oBAAa,CAAb,YAAa,CACb,6BAAsB,CAAtB,qBAAsB,CACtB,0BAAmB,CAAnB,kBAAmB,CACnB,8BAAuB,CAAvB,sBAAuB,CACvB,4BAA6B,CAC7B,UACF,CAEA,UACE,aACF,CAEA,iCACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF,CAPA,yBACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF,CAEA,OACE,SACF,CAEA,YACE,oBAAa,CAAb,YAAa,CACb,8BAAuB,CAAvB,sBAAuB,CACvB,sBAAe,CAAf,cAAe,CACf,WACF,CAEA,gBACE,WACF,CAEA,UACE,WACF,CAEA,cACE,eACF,CAEA,uBACE,kBAAgC,CAAhC,+BAAgC,CAChC,UAAW,CACX,WAAY,CACZ,eACF,CAEA,aACE,yBAA2B,CAC3B,eAAmB,CACnB,WACF,CAEA,WACE,kBAAgC,CAAhC,+BAAgC,CAChC,oBAAuB,CACvB,YAAa,CACb,YACF,CAEA,wBACE,cAAgB,CAChB,kBAAoB,CACpB,WAAY,CACZ,eAAiB,CACjB,wBAAyB,CACzB,qBACF,CAEA,mBACE,kBAAmB,CACnB,mBACF,CAEA,oBACE,mBACF,CAEA,iBACE,eACF,CAEA,UACE,wBAAsC,CAAtC,qCAAsC,CACtC,mBACF,CAEA,eACE,kBAAgC,CAAhC,+BAAgC,CAChC,UAAY,CACZ,eAAiB,CACjB,wBAAyB,CACzB,oBACF,CAEA,iBACE,8BAAuB,CAAvB,sBAAuB,CACvB,SAAU,CACV,gBACF,CAEA,oBACE,eAAiB,CACjB,UAAY,CACZ,iBACF,CAEA,QACE,WAAY,CACZ,gBACF,CAEA,SACE,aAAe,CACf,kBAAgC,CAAhC,+BACF,CAEA,aACE,8BAAuB,CAAvB,sBAAuB,CACvB,SAAU,CACV,gBACF,CAEA,kBACE,gBACF","file":"main.895ab0f5.chunk.css","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n\nform {\n display: flex;\n max-width: 500px;\n margin: auto;\n flex-direction: column;\n}\n\nform label {\n width: 100%; \n display: flex;\n flex-direction: row;\n justify-content: space-between;\n margin: auto;\n}\n\n",":root {\n --color-primary: #282c34;\n}\n\n.App-header {\n background-color: var(--color-primary);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n.error {\n color: red;\n}\n\n.photo-wall {\n display: flex;\n justify-content: center;\n flex-wrap: wrap;\n margin: 2rem;\n}\n\n.dashboard-form {\n margin: auto;\n}\n\n.ant-card {\n margin: 1rem;\n}\n\n.ant-card img {\n max-width: 250px;\n}\n\n.loading-bar-container {\n background: var(--color-primary);\n width: 100%;\n height: 30px;\n min-height: 30px;\n}\n\n.loading-bar {\n transition: all 0.5s linear;\n background: #66ff66;\n height: 100%;\n}\n\n.page-head {\n background: var(--color-primary);\n color: white !important;\n margin-top: 0;\n padding: 1rem;\n}\n\n.session-toolbar button {\n margin: 0 0.5rem;\n padding: 0.5rem 1rem;\n height: 100%;\n font-weight: bold;\n text-transform: uppercase;\n letter-spacing: 0.05rem;\n}\n\n.ant-modal-content {\n border-radius: 1rem;\n padding: 0 !important;\n}\n\n.ant-modal-body img {\n border-radius: 0.5rem;\n}\n\n.ant-modal-close {\n font-weight: bold;\n}\n\n.ant-card {\n border: 1px solid var(--color-primary);\n border-radius: 0.2rem;\n}\n\n.ant-card-head {\n background: var(--color-primary);\n color: white;\n font-weight: bold;\n text-transform: uppercase;\n letter-spacing: 0.2rem;\n}\n\n.session-toolbar {\n justify-content: center;\n width: 60%;\n margin: 1rem auto;\n}\n\n.session-toolbar h3 {\n font-weight: bold;\n color: white;\n margin-right: 1rem;\n}\n\n.slider {\n width: 400px;\n margin: auto 1rem;\n}\n\n.toolbar {\n padding: 0.5rem;\n background: var(--color-primary);\n}\n\n.client-info {\n justify-content: center;\n width: 60%;\n margin: 1rem auto;\n}\n\n.client-info span {\n margin: auto 1rem;\n}\n"]}

View File

@@ -1,2 +0,0 @@
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}form{max-width:500px;-webkit-flex-direction:column;flex-direction:column}form,form label{display:-webkit-flex;display:flex;margin:auto}form label{width:100%;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:space-between;justify-content:space-between}.App{text-align:center}.App-logo{height:40vmin;pointer-events:none}@media (prefers-reduced-motion:no-preference){.App-logo{-webkit-animation:App-logo-spin 20s linear infinite;animation:App-logo-spin 20s linear infinite}}.App-header{background-color:#282c34;min-height:100vh;display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;font-size:calc(10px + 2vmin);color:#fff}.App-link{color:#61dafb}@-webkit-keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}
/*# sourceMappingURL=main.cada1265.chunk.css.map */

View File

@@ -1 +0,0 @@
{"version":3,"sources":["webpack://src/index.css","webpack://src/App.css"],"names":[],"mappings":"AAAA,KACE,QAAS,CACT,mJAEY,CACZ,kCAAmC,CACnC,iCACF,CAEA,KACE,yEAEF,CAEA,KAEE,eAAgB,CAEhB,6BAAsB,CAAtB,qBACF,CAEA,gBANE,oBAAa,CAAb,YAAa,CAEb,WAUF,CANA,WACE,UAAW,CAEX,0BAAmB,CAAnB,kBAAmB,CACnB,qCAA8B,CAA9B,6BAEF,CC3BA,KACE,iBACF,CAEA,UACE,aAAc,CACd,mBACF,CAEA,8CACE,UACE,mDAA4C,CAA5C,2CACF,CACF,CAEA,YACE,wBAAyB,CACzB,gBAAiB,CACjB,oBAAa,CAAb,YAAa,CACb,6BAAsB,CAAtB,qBAAsB,CACtB,0BAAmB,CAAnB,kBAAmB,CACnB,8BAAuB,CAAvB,sBAAuB,CACvB,4BAA6B,CAC7B,UACF,CAEA,UACE,aACF,CAEA,iCACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF,CAPA,yBACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF","file":"main.cada1265.chunk.css","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n\nform {\n display: flex;\n max-width: 500px;\n margin: auto;\n flex-direction: column;\n}\n\nform label {\n width: 100%; \n display: flex;\n flex-direction: row;\n justify-content: space-between;\n margin: auto;\n}\n\n",".App {\n text-align: center;\n}\n\n.App-logo {\n height: 40vmin;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .App-logo {\n animation: App-logo-spin infinite 20s linear;\n }\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,12 @@ object-assign
@license MIT @license MIT
*/ */
/*!
Copyright (c) 2017 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/** @license React v0.20.1 /** @license React v0.20.1
* scheduler.production.min.js * scheduler.production.min.js
* *

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
(this.webpackJsonpclient=this.webpackJsonpclient||[]).push([[3],{399:function(t,n,e){"use strict";e.r(n),e.d(n,"getCLS",(function(){return v})),e.d(n,"getFCP",(function(){return y})),e.d(n,"getFID",(function(){return k})),e.d(n,"getLCP",(function(){return C})),e.d(n,"getTTFB",(function(){return P}));var i,a,r,o,c=function(t,n){return{name:t,value:void 0===n?-1:n,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},u=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},f=!1,s=function(t,n){f||"undefined"!=typeof InstallTrigger||(addEventListener("beforeunload",(function(){})),f=!0),addEventListener("visibilitychange",(function e(i){"hidden"===document.visibilityState&&(t(i),n&&removeEventListener("visibilitychange",e,!0))}),!0)},d=function(t){addEventListener("pageshow",(function(n){n.persisted&&t(n)}),!0)},m="function"==typeof WeakSet?new WeakSet:new Set,p=function(t,n,e){var i;return function(){n.value>=0&&(e||m.has(n)||"hidden"===document.visibilityState)&&(n.delta=n.value-(i||0),(n.delta||void 0===i)&&(i=n.value,t(n)))}},v=function(t,n){var e,i=c("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),e())},r=u("layout-shift",a);r&&(e=p(t,i,n),s((function(){r.takeRecords().map(a),e()})),d((function(){i=c("CLS",0),e=p(t,i,n)})))},l=-1,h=function(){return"hidden"===document.visibilityState?0:1/0},S=function(){s((function(t){var n=t.timeStamp;l=n}),!0)},g=function(){return l<0&&(l=h(),S(),d((function(){setTimeout((function(){l=h(),S()}),0)}))),{get timeStamp(){return l}}},y=function(t,n){var e,i=g(),a=c("FCP"),r=u("paint",(function(t){"first-contentful-paint"===t.name&&(r&&r.disconnect(),t.startTime<i.timeStamp&&(a.value=t.startTime,a.entries.push(t),m.add(a),e()))}));r&&(e=p(t,a,n),d((function(i){a=c("FCP"),e=p(t,a,n),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),e()}))}))})))},w={passive:!0,capture:!0},E=new Date,L=function(t,n){i||(i=n,a=t,r=new Date,F(removeEventListener),T())},T=function(){if(a>=0&&a<r-E){var t={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+a};o.forEach((function(n){n(t)})),o=[]}},b=function(t){if(t.cancelable){var n=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,n){var e=function(){L(t,n),a()},i=function(){a()},a=function(){removeEventListener("pointerup",e,w),removeEventListener("pointercancel",i,w)};addEventListener("pointerup",e,w),addEventListener("pointercancel",i,w)}(n,t):L(n,t)}},F=function(t){["mousedown","keydown","touchstart","pointerdown"].forEach((function(n){return t(n,b,w)}))},k=function(t,n){var e,r=g(),f=c("FID"),v=function(t){t.startTime<r.timeStamp&&(f.value=t.processingStart-t.startTime,f.entries.push(t),m.add(f),e())},l=u("first-input",v);e=p(t,f,n),l&&s((function(){l.takeRecords().map(v),l.disconnect()}),!0),l&&d((function(){var r;f=c("FID"),e=p(t,f,n),o=[],a=-1,i=null,F(addEventListener),r=v,o.push(r),T()}))},C=function(t,n){var e,i=g(),a=c("LCP"),r=function(t){var n=t.startTime;n<i.timeStamp&&(a.value=n,a.entries.push(t)),e()},o=u("largest-contentful-paint",r);if(o){e=p(t,a,n);var f=function(){m.has(a)||(o.takeRecords().map(r),o.disconnect(),m.add(a),e())};["keydown","click"].forEach((function(t){addEventListener(t,f,{once:!0,capture:!0})})),s(f,!0),d((function(i){a=c("LCP"),e=p(t,a,n),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),e()}))}))}))}},P=function(t){var n,e=c("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)}}}]);
//# sourceMappingURL=3.8e9312c3.chunk.js.map

View File

@@ -1,2 +0,0 @@
(this.webpackJsonpclient=this.webpackJsonpclient||[]).push([[3],{64:function(t,n,e){"use strict";e.r(n),e.d(n,"getCLS",(function(){return v})),e.d(n,"getFCP",(function(){return y})),e.d(n,"getFID",(function(){return k})),e.d(n,"getLCP",(function(){return C})),e.d(n,"getTTFB",(function(){return P}));var i,a,r,o,c=function(t,n){return{name:t,value:void 0===n?-1:n,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},u=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},f=!1,s=function(t,n){f||"undefined"!=typeof InstallTrigger||(addEventListener("beforeunload",(function(){})),f=!0),addEventListener("visibilitychange",(function e(i){"hidden"===document.visibilityState&&(t(i),n&&removeEventListener("visibilitychange",e,!0))}),!0)},d=function(t){addEventListener("pageshow",(function(n){n.persisted&&t(n)}),!0)},m="function"==typeof WeakSet?new WeakSet:new Set,p=function(t,n,e){var i;return function(){n.value>=0&&(e||m.has(n)||"hidden"===document.visibilityState)&&(n.delta=n.value-(i||0),(n.delta||void 0===i)&&(i=n.value,t(n)))}},v=function(t,n){var e,i=c("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),e())},r=u("layout-shift",a);r&&(e=p(t,i,n),s((function(){r.takeRecords().map(a),e()})),d((function(){i=c("CLS",0),e=p(t,i,n)})))},l=-1,h=function(){return"hidden"===document.visibilityState?0:1/0},S=function(){s((function(t){var n=t.timeStamp;l=n}),!0)},g=function(){return l<0&&(l=h(),S(),d((function(){setTimeout((function(){l=h(),S()}),0)}))),{get timeStamp(){return l}}},y=function(t,n){var e,i=g(),a=c("FCP"),r=u("paint",(function(t){"first-contentful-paint"===t.name&&(r&&r.disconnect(),t.startTime<i.timeStamp&&(a.value=t.startTime,a.entries.push(t),m.add(a),e()))}));r&&(e=p(t,a,n),d((function(i){a=c("FCP"),e=p(t,a,n),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),e()}))}))})))},w={passive:!0,capture:!0},E=new Date,L=function(t,n){i||(i=n,a=t,r=new Date,F(removeEventListener),T())},T=function(){if(a>=0&&a<r-E){var t={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+a};o.forEach((function(n){n(t)})),o=[]}},b=function(t){if(t.cancelable){var n=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,n){var e=function(){L(t,n),a()},i=function(){a()},a=function(){removeEventListener("pointerup",e,w),removeEventListener("pointercancel",i,w)};addEventListener("pointerup",e,w),addEventListener("pointercancel",i,w)}(n,t):L(n,t)}},F=function(t){["mousedown","keydown","touchstart","pointerdown"].forEach((function(n){return t(n,b,w)}))},k=function(t,n){var e,r=g(),f=c("FID"),v=function(t){t.startTime<r.timeStamp&&(f.value=t.processingStart-t.startTime,f.entries.push(t),m.add(f),e())},l=u("first-input",v);e=p(t,f,n),l&&s((function(){l.takeRecords().map(v),l.disconnect()}),!0),l&&d((function(){var r;f=c("FID"),e=p(t,f,n),o=[],a=-1,i=null,F(addEventListener),r=v,o.push(r),T()}))},C=function(t,n){var e,i=g(),a=c("LCP"),r=function(t){var n=t.startTime;n<i.timeStamp&&(a.value=n,a.entries.push(t)),e()},o=u("largest-contentful-paint",r);if(o){e=p(t,a,n);var f=function(){m.has(a)||(o.takeRecords().map(r),o.disconnect(),m.add(a),e())};["keydown","click"].forEach((function(t){addEventListener(t,f,{once:!0,capture:!0})})),s(f,!0),d((function(i){a=c("LCP"),e=p(t,a,n),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),e()}))}))}))}},P=function(t){var n,e=c("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)}}}]);
//# sourceMappingURL=3.feab56bb.chunk.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
!function(e){function t(t){for(var n,i,a=t[0],c=t[1],l=t[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(t);p.length;)p.shift()();return u.push.apply(u,l||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var c=r[a];0!==o[c]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(e){var t=[],r=o[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=o[e]=[t,n]}));t.push(r[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"feab56bb"}[e]+".chunk.js"}(e);var c=new Error;u=function(t){a.onerror=a.onload=null,clearTimeout(l);var r=o[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),u=t&&t.target&&t.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,r[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(t)},i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpclient=this.webpackJsonpclient||[],c=a.push.bind(a);a.push=t,a=a.slice();for(var l=0;l<a.length;l++)t(a[l]);var f=c;r()}([]); !function(e){function t(t){for(var n,i,a=t[0],c=t[1],l=t[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(t);p.length;)p.shift()();return u.push.apply(u,l||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var c=r[a];0!==o[c]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(e){var t=[],r=o[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=o[e]=[t,n]}));t.push(r[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"8e9312c3"}[e]+".chunk.js"}(e);var c=new Error;u=function(t){a.onerror=a.onload=null,clearTimeout(l);var r=o[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),u=t&&t.target&&t.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,r[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(t)},i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpclient=this.webpackJsonpclient||[],c=a.push.bind(a);a.push=t,a=a.slice();for(var l=0;l<a.length;l++)t(a[l]);var f=c;r()}([]);
//# sourceMappingURL=runtime-main.e7edc0d2.js.map //# sourceMappingURL=runtime-main.8d7962eb.js.map

176
client/package-lock.json generated
View File

@@ -49,9 +49,6 @@
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
},
"peerDependencies": {
"react": ">=16.0.0"
} }
}, },
"node_modules/@ant-design/icons-svg": { "node_modules/@ant-design/icons-svg": {
@@ -1618,7 +1615,6 @@
"jest-resolve": "^26.6.2", "jest-resolve": "^26.6.2",
"jest-util": "^26.6.2", "jest-util": "^26.6.2",
"jest-worker": "^26.6.2", "jest-worker": "^26.6.2",
"node-notifier": "^8.0.0",
"slash": "^3.0.0", "slash": "^3.0.0",
"source-map": "^0.6.0", "source-map": "^0.6.0",
"string-length": "^4.0.1", "string-length": "^4.0.1",
@@ -3011,14 +3007,6 @@
"rc-util": "^5.8.1", "rc-util": "^5.8.1",
"scroll-into-view-if-needed": "^2.2.25", "scroll-into-view-if-needed": "^2.2.25",
"warning": "^4.0.3" "warning": "^4.0.3"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ant-design"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/anymatch": { "node_modules/anymatch": {
@@ -5387,18 +5375,8 @@
"integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==", "integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==",
"engines": { "engines": {
"node": ">=0.11" "node": ">=0.11"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/date-fns"
} }
}, },
"node_modules/dayjs": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz",
"integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==",
"peer": true
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
@@ -9601,7 +9579,6 @@
"@types/node": "*", "@types/node": "*",
"anymatch": "^3.0.3", "anymatch": "^3.0.3",
"fb-watchman": "^2.0.0", "fb-watchman": "^2.0.0",
"fsevents": "^2.1.2",
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
"jest-regex-util": "^26.0.0", "jest-regex-util": "^26.0.0",
"jest-serializer": "^26.6.2", "jest-serializer": "^26.6.2",
@@ -10898,10 +10875,6 @@
"dependencies": { "dependencies": {
"hoist-non-react-statics": "^3.3.2", "hoist-non-react-statics": "^3.3.2",
"shallowequal": "^1.0.2" "shallowequal": "^1.0.2"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/minimalistic-assert": { "node_modules/minimalistic-assert": {
@@ -13622,10 +13595,6 @@
"dom-align": "^1.7.0", "dom-align": "^1.7.0",
"rc-util": "^5.3.0", "rc-util": "^5.3.0",
"resize-observer-polyfill": "^1.5.1" "resize-observer-polyfill": "^1.5.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-cascader": { "node_modules/rc-cascader": {
@@ -13638,10 +13607,6 @@
"rc-trigger": "^5.0.4", "rc-trigger": "^5.0.4",
"rc-util": "^5.0.1", "rc-util": "^5.0.1",
"warning": "^4.0.1" "warning": "^4.0.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-checkbox": { "node_modules/rc-checkbox": {
@@ -13651,10 +13616,6 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.1" "classnames": "^2.2.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-collapse": { "node_modules/rc-collapse": {
@@ -13667,10 +13628,6 @@
"rc-motion": "^2.3.4", "rc-motion": "^2.3.4",
"rc-util": "^5.2.1", "rc-util": "^5.2.1",
"shallowequal": "^1.1.0" "shallowequal": "^1.1.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-dialog": { "node_modules/rc-dialog": {
@@ -13682,10 +13639,6 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"rc-motion": "^2.3.0", "rc-motion": "^2.3.0",
"rc-util": "^5.6.1" "rc-util": "^5.6.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-drawer": { "node_modules/rc-drawer": {
@@ -13696,10 +13649,6 @@
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"rc-util": "^5.7.0" "rc-util": "^5.7.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-dropdown": { "node_modules/rc-dropdown": {
@@ -13710,10 +13659,6 @@
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"rc-trigger": "^5.0.4" "rc-trigger": "^5.0.4"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
} }
}, },
"node_modules/rc-field-form": { "node_modules/rc-field-form": {
@@ -13727,10 +13672,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">= 16.9.0",
"react-dom": ">= 16.9.0"
} }
}, },
"node_modules/rc-image": { "node_modules/rc-image": {
@@ -13742,10 +13683,6 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"rc-dialog": "~8.5.0", "rc-dialog": "~8.5.0",
"rc-util": "^5.0.6" "rc-util": "^5.0.6"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-input-number": { "node_modules/rc-input-number": {
@@ -13756,10 +13693,6 @@
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"rc-util": "^5.0.1" "rc-util": "^5.0.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-mentions": { "node_modules/rc-mentions": {
@@ -13773,10 +13706,6 @@
"rc-textarea": "^0.3.0", "rc-textarea": "^0.3.0",
"rc-trigger": "^5.0.4", "rc-trigger": "^5.0.4",
"rc-util": "^5.0.1" "rc-util": "^5.0.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-menu": { "node_modules/rc-menu": {
@@ -13792,10 +13721,6 @@
"rc-util": "^5.7.0", "rc-util": "^5.7.0",
"resize-observer-polyfill": "^1.5.0", "resize-observer-polyfill": "^1.5.0",
"shallowequal": "^1.1.0" "shallowequal": "^1.1.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-motion": { "node_modules/rc-motion": {
@@ -13806,10 +13731,6 @@
"@babel/runtime": "^7.11.1", "@babel/runtime": "^7.11.1",
"classnames": "^2.2.1", "classnames": "^2.2.1",
"rc-util": "^5.2.1" "rc-util": "^5.2.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-notification": { "node_modules/rc-notification": {
@@ -13824,10 +13745,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-overflow": { "node_modules/rc-overflow": {
@@ -13839,10 +13756,6 @@
"classnames": "^2.2.1", "classnames": "^2.2.1",
"rc-resize-observer": "^1.0.0", "rc-resize-observer": "^1.0.0",
"rc-util": "^5.5.1" "rc-util": "^5.5.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-pagination": { "node_modules/rc-pagination": {
@@ -13852,10 +13765,6 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.1" "classnames": "^2.2.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-picker": { "node_modules/rc-picker": {
@@ -13873,11 +13782,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"dayjs": "^1.8.30",
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-progress": { "node_modules/rc-progress": {
@@ -13887,10 +13791,6 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.6" "classnames": "^2.2.6"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-rate": { "node_modules/rc-rate": {
@@ -13904,10 +13804,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-resize-observer": { "node_modules/rc-resize-observer": {
@@ -13919,10 +13815,6 @@
"classnames": "^2.2.1", "classnames": "^2.2.1",
"rc-util": "^5.0.0", "rc-util": "^5.0.0",
"resize-observer-polyfill": "^1.5.1" "resize-observer-polyfill": "^1.5.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-select": { "node_modules/rc-select": {
@@ -13940,10 +13832,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
} }
}, },
"node_modules/rc-slider": { "node_modules/rc-slider": {
@@ -13959,10 +13847,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-steps": { "node_modules/rc-steps": {
@@ -13976,10 +13860,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-switch": { "node_modules/rc-switch": {
@@ -13990,10 +13870,6 @@
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.1", "classnames": "^2.2.1",
"rc-util": "^5.0.1" "rc-util": "^5.0.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-table": { "node_modules/rc-table": {
@@ -14009,10 +13885,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-tabs": { "node_modules/rc-tabs": {
@@ -14029,10 +13901,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-textarea": { "node_modules/rc-textarea": {
@@ -14044,10 +13912,6 @@
"classnames": "^2.2.1", "classnames": "^2.2.1",
"rc-resize-observer": "^1.0.0", "rc-resize-observer": "^1.0.0",
"rc-util": "^5.7.0" "rc-util": "^5.7.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-tooltip": { "node_modules/rc-tooltip": {
@@ -14057,10 +13921,6 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.11.2", "@babel/runtime": "^7.11.2",
"rc-trigger": "^5.0.0" "rc-trigger": "^5.0.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-tree": { "node_modules/rc-tree": {
@@ -14076,10 +13936,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
} }
}, },
"node_modules/rc-tree-select": { "node_modules/rc-tree-select": {
@@ -14092,10 +13948,6 @@
"rc-select": "^12.0.0", "rc-select": "^12.0.0",
"rc-tree": "^4.0.0", "rc-tree": "^4.0.0",
"rc-util": "^5.0.5" "rc-util": "^5.0.5"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
} }
}, },
"node_modules/rc-trigger": { "node_modules/rc-trigger": {
@@ -14111,10 +13963,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-upload": { "node_modules/rc-upload": {
@@ -14125,10 +13973,6 @@
"@babel/runtime": "^7.10.1", "@babel/runtime": "^7.10.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"rc-util": "^5.2.0" "rc-util": "^5.2.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-util": { "node_modules/rc-util": {
@@ -14139,10 +13983,6 @@
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"react-is": "^16.12.0", "react-is": "^16.12.0",
"shallowequal": "^1.1.0" "shallowequal": "^1.1.0"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
} }
}, },
"node_modules/rc-util/node_modules/react-is": { "node_modules/rc-util/node_modules/react-is": {
@@ -14161,10 +14001,6 @@
}, },
"engines": { "engines": {
"node": ">=8.x" "node": ">=8.x"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
} }
}, },
"node_modules/react": { "node_modules/react": {
@@ -14485,7 +14321,6 @@
"eslint-webpack-plugin": "^2.5.2", "eslint-webpack-plugin": "^2.5.2",
"file-loader": "6.1.1", "file-loader": "6.1.1",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"fsevents": "^2.1.3",
"html-webpack-plugin": "4.5.0", "html-webpack-plugin": "4.5.0",
"identity-obj-proxy": "3.0.0", "identity-obj-proxy": "3.0.0",
"jest": "26.6.0", "jest": "26.6.0",
@@ -17652,10 +17487,8 @@
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
"integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
"dependencies": { "dependencies": {
"chokidar": "^3.4.1",
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"neo-async": "^2.5.0", "neo-async": "^2.5.0"
"watchpack-chokidar2": "^2.0.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"chokidar": "^3.4.1", "chokidar": "^3.4.1",
@@ -18151,7 +17984,6 @@
"anymatch": "^2.0.0", "anymatch": "^2.0.0",
"async-each": "^1.0.1", "async-each": "^1.0.1",
"braces": "^2.3.2", "braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0", "glob-parent": "^3.1.0",
"inherits": "^2.0.3", "inherits": "^2.0.3",
"is-binary-path": "^1.0.0", "is-binary-path": "^1.0.0",
@@ -24165,12 +23997,6 @@
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz",
"integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==" "integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg=="
}, },
"dayjs": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz",
"integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==",
"peer": true
},
"debug": { "debug": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",

View File

@@ -1,20 +1,9 @@
.App { :root {
text-align: center; --color-primary: #282c34;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
} }
.App-header { .App-header {
background-color: #282c34; background-color: var(--color-primary);
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -40,3 +29,109 @@
.error { .error {
color: red; color: red;
} }
.photo-wall {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin: 2rem;
}
.dashboard-form {
margin: auto;
}
.ant-card {
margin: 1rem;
}
.ant-card img {
max-width: 250px;
}
.loading-bar-container {
background: var(--color-primary);
width: 100%;
height: 30px;
min-height: 30px;
}
.loading-bar {
transition: all 0.5s linear;
background: #66ff66;
height: 100%;
}
.page-head {
background: var(--color-primary);
color: white !important;
margin-top: 0;
padding: 1rem;
}
.session-toolbar button {
margin: 0 0.5rem;
padding: 0.5rem 1rem;
height: 100%;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.05rem;
}
.ant-modal-content {
border-radius: 1rem;
padding: 0 !important;
}
.ant-modal-body img {
border-radius: 0.5rem;
}
.ant-modal-close {
font-weight: bold;
}
.ant-card {
border: 1px solid var(--color-primary);
border-radius: 0.2rem;
}
.ant-card-head {
background: var(--color-primary);
color: white;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.2rem;
}
.session-toolbar {
justify-content: center;
width: 60%;
margin: 1rem auto;
}
.session-toolbar h3 {
font-weight: bold;
color: white;
margin-right: 1rem;
}
.slider {
width: 400px;
margin: auto 1rem;
}
.toolbar {
padding: 0.5rem;
background: var(--color-primary);
}
.client-info {
justify-content: center;
width: 60%;
margin: 1rem auto;
}
.client-info span {
margin: auto 1rem;
}

View File

@@ -5,6 +5,8 @@ import { BrowserRouter, Switch, Route } from 'react-router-dom'
import { Dashboard } from './pages/Dashboard' import { Dashboard } from './pages/Dashboard'
import { Session } from './pages/Session' import { Session } from './pages/Session'
console.log('ENV', process.env.NODE_ENV)
function App() { function App() {
return ( return (
<BrowserRouter> <BrowserRouter>

View File

@@ -1,36 +1,72 @@
import { Client } from '../types' import { Client, Timings } from '../types'
import axios from 'axios' import axios from 'axios'
import { client, clients } from '../data' import { message } from 'antd'
import settings from '../settings' import { Status } from '../components/StatusChip'
const apiUrl = '/api' const dev = process.env.NODE_ENV === 'development'
export const createClient = async (body: Omit<Client, 'photos'>) => { if (dev) {
await axios.post(`${apiUrl}/clients`, body) const host = 'http://192.168.1.114:5000'
axios.defaults.baseURL = host
} }
export const getClients = async (): Promise<Client[]> => { const mock = false
if (settings.env === 'jank') return clients
const res = await axios.post<Client[]>(`${apiUrl}/clients`) export const createClient = async (body: Omit<Client, 'has_photos'>) => {
return res.data if (mock) return 'test'
const res = await axios.post<{ client_id: string }>(`/api/clients`, body)
return res.data.client_id
} }
export const getClient = async (id: string): Promise<Client> => { export const getClient = async (id: string): Promise<Client> => {
if (settings.env === 'jank') return client if (mock)
const res = await axios.post<Client>(`${apiUrl}/client/${id}`) return {
name: 'Test Client',
has_photos: false,
email: 'test@test.test',
phone: 1234567890,
}
const res = await axios.get<Client>(`/api/clients/${id}`)
return res.data return res.data
} }
export const startSession = async (clientId: string) => { export const startSession = async (clientId: string, timings: Timings) => {
const res = await axios.post(`${apiUrl}/clients/${clientId}`) try {
const res = await axios.post(`/api/clients/${clientId}/session`, timings)
return res.data
} catch (err) {
message.error('Something went wrong, check connection with the machine')
return err
}
}
export const getSession = async (clientId: string) => {
const res = await axios.get<{ photos: string[] }>(
`/api/clients/${clientId}/session`,
)
return res.data // session data return res.data // session data
} }
export const killSession = async (clientId: string) => { export const killSession = async (clientId: string) => {
await axios.delete(`${apiUrl}/clients/${clientId}`) await axios.delete(`/api/clients/${clientId}/session`)
} }
export const restartSession = async (clientId: string) => { export const restartSession = async (clientId: string, timings: Timings) => {
await killSession(clientId) await killSession(clientId)
await startSession(clientId) await startSession(clientId, timings)
}
// TOOD: Get status
export const getStatus = async (): Promise<Status> => {
const res = await axios.get<{ status: Status }>('/api/status')
return res.data.status
}
// Someday
export const getClients = async (): Promise<Client[]> => {
const res = await axios.get<Client[]>(`/api/clients`)
return res.data
} }
export const cleanup = () => { export const cleanup = () => {

View File

@@ -0,0 +1,23 @@
import { Button } from 'antd'
import React from 'react'
export const ScrollToTop = () => {
const handleClick = () => {
window.scrollTo(0, 0)
console.log('')
}
return (
<Button
type="link"
style={{
position: 'fixed',
right: 20,
bottom: 20,
}}
onClick={handleClick}
>
Scroll To Top
</Button>
)
}

View File

@@ -0,0 +1,131 @@
import React, { useEffect, useState } from 'react'
import { Card, Modal, Row, Select, Typography } from 'antd'
import { getSession } from '../api'
import { ScrollToTop } from './ScrollToTop'
type Props = {
clientId: string
}
export const SessionPictures = ({ clientId }: Props) => {
const [urls, setUrls] = useState<string[] | null>(null)
const [activeUrl, setActiveUrl] = useState<string | null>(null)
const [focusPhotos, setFocusPhotos] = useState<string[]>(
JSON.parse(window.localStorage.getItem('focusPhotos') || '[]'),
)
useEffect(() => {
const get = async () => {
if (urls && urls.length >= 89 * 1) {
return
}
const { photos } = await getSession(clientId)
if (photos.length) setUrls(photos)
}
const interval = setInterval(get, 250)
return () => clearInterval(interval)
}, [clientId, urls])
const closeModal = () => setActiveUrl(null)
const host =
process.env.NODE_ENV === 'development' ? 'http://192.168.1.107:5000' : ''
if (!urls?.length) return null
const photos = urls.sort((a, b) =>
a.split('_')[0].localeCompare(b.split('_')[0]),
)
const u = urls.length / 89
const handleSelect = (v: string[]) => {
console.log('SEelcted', v)
window.localStorage.setItem('focusPhotos', JSON.stringify(v))
setFocusPhotos(v)
}
const filteredPhotos = photos.filter((name) => {
const num = name.split('_')[0]
return focusPhotos.includes(num)
})
return (
<>
<Modal
visible={!!activeUrl}
onOk={closeModal}
footer={null}
onCancel={closeModal}
width="50%"
>
<img
width="100%"
onClick={closeModal}
src={`${host}/output/${clientId}/${activeUrl}`}
alt="large modal"
></img>
</Modal>
<Row
align="middle"
justify="space-around"
style={{ display: 'flex', width: '100%' }}
>
<Typography.Title style={{ margin: '0.5rem 1rem 0.7rem' }} level={3}>
Session Pictures
</Typography.Title>
<Typography.Text>{urls.length}/ 89 loaded</Typography.Text>
<Typography.Text>Select Featured Photos:</Typography.Text>
<Select
mode="multiple"
allowClear
placeholder="Please select featured"
style={{ width: '35%' }}
defaultValue={focusPhotos}
value={focusPhotos}
onChange={handleSelect}
>
{photos.map((name) => {
const val = name.split('_')[0]
return <Select.Option value={val}>{val}</Select.Option>
})}
</Select>
</Row>
<div className="loading-bar-container">
<div
className="loading-bar"
style={{
width: `${u * 100}%`,
background: `hsl(${Math.floor(u * 120)}, 90%, 70%)`,
}}
></div>
</div>
<div className="featured-photos" style={{ marginTop: '2rem' }}>
{filteredPhotos.map((src) => (
<img
onClick={() => setActiveUrl(src)}
src={`${host}/output/${clientId}/${src}`}
alt="lol"
/>
))}
</div>
<div className="photo-wall">
{photos.map((src) => (
<Card key={src} className="photo" title={src.split('_')[0]}>
<img
onClick={() => setActiveUrl(src)}
src={`${host}/output/${clientId}/${src}`}
alt="lol"
/>
</Card>
))}
</div>
<ScrollToTop />
</>
)
}

View File

@@ -0,0 +1,47 @@
import { Tag } from 'antd'
import { PresetColorType } from 'antd/lib/_util/colors'
import { useEffect, useState } from 'react'
import { getStatus } from '../api'
export enum Status {
'Standing By...',
'Warming Up...',
'Capturing Photo',
'Capturing Grid',
'Writing To Disk',
'Downloading!',
}
const colors: Partial<PresetColorType>[] = [
'lime',
'gold',
'volcano',
'magenta',
'geekblue',
]
type Props = {
poll: boolean
}
export const StatusChip = ({ poll }: Props) => {
const [status, setStatus] = useState<Status>(Status['Standing By...'])
useEffect(() => {
const get = async () => {
if (!poll) return
const status = await getStatus()
setStatus(status)
}
const interval = setInterval(get, 1000 / 4)
return () => clearInterval(interval)
}, [poll])
return (
<Tag color={colors[status]} style={{ display: 'flex' }}>
<span style={{ margin: 'auto' }}>{Status[status]}</span>
</Tag>
)
}

View File

@@ -5,18 +5,17 @@ export const clients: Client[] = [
name: 'Elijah', name: 'Elijah',
email: 'elijah@elijah.com', email: 'elijah@elijah.com',
phone: 4039876543, phone: 4039876543,
photos: [], has_photos: false,
}, },
{ {
name: 'Tanner', name: 'Tanner',
email: 'tanner@tanner.com', email: 'tanner@tanner.com',
phone: 4031234567, phone: 4031234567,
photos: [ has_photos: true,
'/images/1.jpg',
'/images/2.jpg',
'/images/3.jpg',
'/images/4.jpg',
],
}, },
] ]
export const client: Client = clients[0] export const client: Client = clients[0]
export const session: { photos: string[] } = {
photos: ['/images/1.jpg', '/images/2.jpg', '/images/3.jpg', '/images/4.jpg'],
}

View File

@@ -1,81 +1,78 @@
import { message } from 'antd' import React from 'react'
import { Button, Divider, Form, Input, message, Row, Typography } from 'antd'
import FormItem from 'antd/lib/form/FormItem'
import { Content } from 'antd/lib/layout/layout' import { Content } from 'antd/lib/layout/layout'
import React, { FormEvent } from 'react'
import { useState } from 'react' import { useState } from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import { createClient } from '../api' import { createClient } from '../api'
import settings from '../settings'
type FormData = {
name: string
email: string
phone: string
}
export const Dashboard = () => { export const Dashboard = () => {
const history = useHistory() const history = useHistory()
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [name, setName] = useState('') const [form] = Form.useForm<FormData>()
const [email, setEmail] = useState('')
const [phone, setPhone] = useState('')
const handleReset = () => { const handleReset = () => {
// form.resetFields()
setName('')
setEmail('')
setPhone('')
} }
const handleSubmit = async (e: FormEvent) => { const handleSubmit = async (values: FormData) => {
e.preventDefault() if (values.phone.length < 10) {
if (phone.length < 10) {
// helpful message // helpful message
message.error('Check all fields!') message.error('Check all fields!')
setError('Phone number needs to be a length of at least 10') setError('Phone number needs to be a length of at least 10')
return return
} }
if (settings.env === 'jank') { const client_id = await createClient({
history.push(`/sessions/${phone}`) name: values.name,
return email: values.email,
} phone: parseInt(values.phone.replace(/\D/g, '')),
})
await createClient({ name, email, phone: parseInt(phone) }) history.push(`/sessions/${client_id}`)
history.push(`/sessions/${phone}`)
} }
return ( return (
<Content> <Content>
<h1>Dashboard</h1> <Typography.Title className="page-head" level={3}>
<form onSubmit={handleSubmit}> Dashboard
<label htmlFor="name"> </Typography.Title>
Name: <Divider />
<input <Form
minLength={3} form={form}
value={name} className="dashboard-form"
onChange={(e) => setName(e.target.value)} onFinish={handleSubmit}
name="name" labelCol={{ span: 8 }}
/> wrapperCol={{ span: 16 }}
</label> >
<label htmlFor="email"> <Typography.Paragraph style={{ textAlign: 'center' }}>
Email: Enter the name, email and phone number of the subject
<input </Typography.Paragraph>
value={email} <FormItem label="name" name="name">
onChange={(e) => setEmail(e.target.value)} <Input minLength={3} />
type="email" </FormItem>
name="email" <FormItem label="email" name="email">
/> <Input type="email" />
</label> </FormItem>
<label htmlFor="phone"> <FormItem label="phone" name="phone">
Phone: <Input type="tel" minLength={10} />
<input </FormItem>
value={phone} <Row justify="space-between">
onChange={(e) => setPhone(e.target.value)} <Button danger onClick={handleReset}>
type="tel"
name="phone"
/>
</label>
<button type="submit">Start Session</button>
<button type="button" onClick={handleReset}>
Reset Reset
</button> </Button>
<Button htmlType="submit" type="primary">
Start Session
</Button>
</Row>
{error && <p className="error">{error}</p>} {error && <p className="error">{error}</p>}
</form> </Form>
</Content> </Content>
) )
} }

View File

@@ -1,24 +1,49 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { RouteComponentProps, useHistory } from 'react-router-dom' import { RouteComponentProps, useHistory } from 'react-router-dom'
import { getClient, killSession, restartSession } from '../api' import { getClient, killSession, restartSession, startSession } from '../api'
import { SessionPictures } from './SessionPictures' import { SessionPictures } from '../components/SessionPictures'
import { Button, Divider, message, PageHeader, Popconfirm, Row } from 'antd' import { StatusChip } from '../components/StatusChip'
import {
Button,
Input,
InputNumber,
message,
Popconfirm,
Row,
Typography,
} from 'antd'
import { Content } from 'antd/lib/layout/layout' import { Content } from 'antd/lib/layout/layout'
import { Client } from '../types'
type Props = RouteComponentProps<{ clientId: string }> type Props = RouteComponentProps<{ clientId: string }>
export const Session = (props: Props) => { export const Session = (props: Props) => {
const history = useHistory() const history = useHistory()
const { clientId } = props.match.params const { clientId } = props.match.params
const [client, setClient] = useState<Client | null>(null)
const [active, setActive] = useState(false) const [active, setActive] = useState(false)
const [lightTime, setLightTime] = useState(
parseInt(window.localStorage.getItem('lightTime') || '5000'),
)
const handleTimingUpdate = (n: number) => {
window.localStorage.setItem('lightTime', n.toString())
setLightTime(n)
}
const handleStartSession = async () => { const handleStartSession = async () => {
message.loading('Photo sequence starting! Stand by...')
await startSession(clientId, { light_time: lightTime })
setActive(true) setActive(true)
} }
const handleRestartSession = async () => { const handleRestartSession = async () => {
setActive(false) setActive(false)
await restartSession(clientId) message.loading(
'Deleting photos & restarting capture sequence! Stand by...',
)
await restartSession(clientId, { light_time: lightTime })
setActive(true) setActive(true)
} }
@@ -28,27 +53,42 @@ export const Session = (props: Props) => {
const handleNuke = async () => { const handleNuke = async () => {
await killSession(clientId) await killSession(clientId)
message.success('Photos Deleted! Going back to dashboard')
history.push('/') history.push('/')
} }
useEffect(() => { useEffect(() => {
const get = async () => { const get = async () => {
const { photos } = await getClient(clientId) const client = await getClient(clientId)
if (photos.length) setActive(true) setClient(client)
else message.info("Click 'Capture' to take a photo set!") if (client.has_photos) setActive(true)
} }
get() get()
}) }, [clientId])
return ( return (
<Content> <Content>
<PageHeader <Typography.Title className="page-head" level={3}>
ghost={false} Session View
onBack={() => history.goBack()} </Typography.Title>
title={`Session for ${clientId}`}
subTitle={`session has ${active ? 'started' : 'not started'}`} <Row className="client-info">
extra={[ <Typography.Text>
<strong>Name:</strong> {client?.name}
</Typography.Text>
<Typography.Text>
<strong>Email:</strong> {client?.email}
</Typography.Text>
<Typography.Text>
<strong>Phone:</strong> {client?.phone}
</Typography.Text>
</Row>
<div className="toolbar">
<Row justify="center" className="session-toolbar">
<Button key="finish" onClick={handleExit}>
Back To Dashboard
</Button>
<Button <Button
key="startsession" key="startsession"
disabled={active} disabled={active}
@@ -56,8 +96,9 @@ export const Session = (props: Props) => {
onClick={handleStartSession} onClick={handleStartSession}
> >
Capture Capture
</Button>, </Button>
<Popconfirm <Popconfirm
disabled={!active}
key="retry" key="retry"
title="Re-capture set?" title="Re-capture set?"
onConfirm={handleRestartSession} onConfirm={handleRestartSession}
@@ -65,28 +106,34 @@ export const Session = (props: Props) => {
<Button type="default" disabled={!active}> <Button type="default" disabled={!active}>
Retry Capture Retry Capture
</Button> </Button>
</Popconfirm>, </Popconfirm>
<Popconfirm <Popconfirm
key="nuke" key="nuke"
title="Delete all photos?" disabled={!active}
title="Delete all photos and return to dashboard?"
onConfirm={handleNuke} onConfirm={handleNuke}
> >
<Button danger disabled={!active}> <Button danger disabled={!active}>
Nuke Session Abort Session
</Button> </Button>
</Popconfirm>, </Popconfirm>
<Button
key="finish" <StatusChip poll={true} />
ghost </Row>
type="primary" <Row className="session-toolbar">
disabled={!active} <h3>Light Duration (ms)</h3>
onClick={handleExit} <InputNumber value={lightTime} onChange={handleTimingUpdate} />
> <Input
Finish Session className="slider"
</Button>, type="range"
]} onChange={(e) => handleTimingUpdate(parseInt(e.target.value))}
></PageHeader> value={lightTime}
<Divider /> min={500}
max={10000}
step={500}
/>
</Row>
</div>
<Row className="controls"> <Row className="controls">
{active && <SessionPictures clientId={clientId} />} {active && <SessionPictures clientId={clientId} />}
</Row> </Row>

View File

@@ -1,34 +0,0 @@
import { Spin } from 'antd'
import React, { useEffect, useState } from 'react'
import { getClient } from '../api'
type Props = {
clientId: string
}
export const SessionPictures = ({ clientId }: Props) => {
const [pics, setPics] = useState<string[] | null>(null)
useEffect(() => {
const get = async () => {
if (pics) return
const { photos } = await getClient(clientId)
if (photos.length) setPics(photos)
}
const interval = setInterval(get, 300)
return () => clearInterval(interval)
}, [clientId, pics])
return (
<div>
<h3>Session Pictures</h3>
{pics ? (
pics.map((src) => <img id={src} src={src} alt="lol" />)
) : (
<Spin />
)}
</div>
)
}

View File

@@ -1,3 +0,0 @@
export default {
env: 'jank',
}

View File

@@ -2,9 +2,13 @@ export type Client = {
name: string name: string
email: string email: string
phone: number phone: number
photos: string[] has_photos: boolean
} }
export type Session = { export type Session = {
timestamp: number timestamp: number
} }
export type Timings = {
light_time: number // 5000
}

View File

@@ -1364,7 +1364,6 @@
"jest-resolve" "^26.6.2" "jest-resolve" "^26.6.2"
"jest-util" "^26.6.2" "jest-util" "^26.6.2"
"jest-worker" "^26.6.2" "jest-worker" "^26.6.2"
"node-notifier" "^8.0.0"
"slash" "^3.0.0" "slash" "^3.0.0"
"source-map" "^0.6.0" "source-map" "^0.6.0"
"string-length" "^4.0.1" "string-length" "^4.0.1"
@@ -3385,7 +3384,6 @@
"anymatch" "^2.0.0" "anymatch" "^2.0.0"
"async-each" "^1.0.1" "async-each" "^1.0.1"
"braces" "^2.3.2" "braces" "^2.3.2"
"fsevents" "^1.2.7"
"glob-parent" "^3.1.0" "glob-parent" "^3.1.0"
"inherits" "^2.0.3" "inherits" "^2.0.3"
"is-binary-path" "^1.0.0" "is-binary-path" "^1.0.0"
@@ -4140,11 +4138,6 @@
"resolved" "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz" "resolved" "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz"
"version" "2.19.0" "version" "2.19.0"
"dayjs@^1.8.30":
"integrity" "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw=="
"resolved" "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz"
"version" "1.10.4"
"debug@^2.2.0": "debug@^2.2.0":
"integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="
"resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
@@ -6667,7 +6660,6 @@
"@types/node" "*" "@types/node" "*"
"anymatch" "^3.0.3" "anymatch" "^3.0.3"
"fb-watchman" "^2.0.0" "fb-watchman" "^2.0.0"
"fsevents" "^2.1.2"
"graceful-fs" "^4.2.4" "graceful-fs" "^4.2.4"
"jest-regex-util" "^26.0.0" "jest-regex-util" "^26.0.0"
"jest-serializer" "^26.6.2" "jest-serializer" "^26.6.2"
@@ -9757,7 +9749,7 @@
"strip-ansi" "6.0.0" "strip-ansi" "6.0.0"
"text-table" "0.2.0" "text-table" "0.2.0"
"react-dom@*", "react-dom@^17.0.1", "react-dom@>= 16.9.0", "react-dom@>=16.9.0": "react-dom@^17.0.1":
"integrity" "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==" "integrity" "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz" "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz"
"version" "17.0.1" "version" "17.0.1"
@@ -9863,7 +9855,6 @@
"eslint-webpack-plugin" "^2.5.2" "eslint-webpack-plugin" "^2.5.2"
"file-loader" "6.1.1" "file-loader" "6.1.1"
"fs-extra" "^9.0.1" "fs-extra" "^9.0.1"
"fsevents" "^2.1.3"
"html-webpack-plugin" "4.5.0" "html-webpack-plugin" "4.5.0"
"identity-obj-proxy" "3.0.0" "identity-obj-proxy" "3.0.0"
"jest" "26.6.0" "jest" "26.6.0"
@@ -9897,7 +9888,7 @@
optionalDependencies: optionalDependencies:
"fsevents" "^2.1.3" "fsevents" "^2.1.3"
"react@*", "react@^17.0.1", "react@>= 16.9.0", "react@>=16.0.0", "react@>=16.9.0": "react@^17.0.1":
"integrity" "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==" "integrity" "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w=="
"resolved" "https://registry.npmjs.org/react/-/react-17.0.1.tgz" "resolved" "https://registry.npmjs.org/react/-/react-17.0.1.tgz"
"version" "17.0.1" "version" "17.0.1"
@@ -12018,10 +12009,8 @@
"resolved" "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz" "resolved" "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz"
"version" "1.7.5" "version" "1.7.5"
dependencies: dependencies:
"chokidar" "^3.4.1"
"graceful-fs" "^4.1.2" "graceful-fs" "^4.1.2"
"neo-async" "^2.5.0" "neo-async" "^2.5.0"
"watchpack-chokidar2" "^2.0.1"
optionalDependencies: optionalDependencies:
"chokidar" "^3.4.1" "chokidar" "^3.4.1"
"watchpack-chokidar2" "^2.0.1" "watchpack-chokidar2" "^2.0.1"

BIN
network-settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

1
server/.gitignore vendored
View File

@@ -103,3 +103,4 @@ ENV/
*.swo *.swo
paramiko.log paramiko.log
output/

View File

@@ -28,3 +28,7 @@ def trigger_capture():
sock.sendto(SEND.encode('utf-8'), (MCAST_GRP, MCAST_PORT)) sock.sendto(SEND.encode('utf-8'), (MCAST_GRP, MCAST_PORT))
sock.close() sock.close()
print('Sent.') print('Sent.')
if __name__ == '__main__':
print('Triggering test capture...')
trigger_capture()

View File

@@ -5,7 +5,18 @@ import paramiko
import time import time
paramiko.util.log_to_file('paramiko.log') paramiko.util.log_to_file('paramiko.log')
def fake_download(ip, dest):
import requests, random
num = ip.split('.')[3]
r = requests.get('https://picsum.photos/400/300?random=' + num)
time.sleep(random.uniform(2, 10))
with open(dest / (num + '_420.jpg'), 'wb') as f:
f.write(r.content)
def download(ip, dest): def download(ip, dest):
if settings.DEBUG:
fake_download(ip, dest)
print('Downloading from', ip) print('Downloading from', ip)
port = 22 port = 22
@@ -18,10 +29,12 @@ def download(ip, dest):
for f in files: for f in files:
source_file = '/3dscan/' + f source_file = '/3dscan/' + f
dest_file = dest + f dest_file = dest / (f + '.tmp')
print('Grabbing file', source_file) print('Grabbing file', source_file)
sftp.get(source_file, dest_file) sftp.get(source_file, dest_file)
sftp.remove(source_file) sftp.remove(source_file)
done_file = dest / f
dest_file.rename(done_file)
if sftp: sftp.close() if sftp: sftp.close()
if transport: transport.close() if transport: transport.close()
@@ -29,10 +42,7 @@ def download(ip, dest):
print('Finished downloading from', ip) print('Finished downloading from', ip)
def download_all_photos(dest): def download_all_photos(dest):
if not dest.endswith('/'): if not dest.exists():
raise Exception('Destination must end with /')
if not os.path.exists(dest):
raise Exception('Destination does not exist') raise Exception('Destination does not exist')
print('Downloading all photos to', dest) print('Downloading all photos to', dest)
@@ -40,3 +50,7 @@ def download_all_photos(dest):
for ip in settings.RASPBERRY_IPS: for ip in settings.RASPBERRY_IPS:
t = threading.Thread(target=download, args=(ip, dest)) t = threading.Thread(target=download, args=(ip, dest))
t.start() t.start()
if __name__ == '__main__':
from pathlib import Path
download_all_photos(Path('test/'))

View File

@@ -1,12 +1,168 @@
from flask import Flask import os
import json
import time
from datetime import datetime
from pathlib import Path
from flask import Flask, request, abort, send_from_directory
from flask_cors import CORS from flask_cors import CORS
build_folder = '../client/build' import power, capture, download, settings
app = Flask(__name__, static_folder=build_folder, static_url_path='')
import logging
log = logging.getLogger('werkzeug')
if not settings.DEBUG:
log.setLevel(logging.ERROR)
build_folder = Path('../client/build')
output_folder = Path('./output')
app = Flask(__name__, static_folder=str(build_folder), static_url_path='')
CORS(app) CORS(app)
STANDBY = 0
WARMUP = 1
CAPTURING_PHOTO = 2
CAPTURING_GRID = 3
WRITING = 4
DOWNLOADING = 5
status = STANDBY
@app.route('/api/status', methods=['GET'])
def status_get():
return {'status': status}
@app.route('/api/clients', methods=['POST'])
def clients_post():
content = request.json
phone = str(content['phone'])
for i in range(1, 100):
suffix = str(i).zfill(2)
folder = phone + '_' + suffix
path = output_folder / folder
if not path.exists():
break
content['date'] = datetime.now().strftime('%Y-%m-%d')
content['time'] = datetime.now().strftime('%H:%M:%S')
path.mkdir()
info_file = path / 'info.txt'
info_file.touch()
info_file.write_text(json.dumps(content, indent=4))
client_id = folder
print('POST client:', content, 'cid:', client_id)
return {'client_id': client_id}
@app.route('/api/clients/<cid>', methods=['GET'])
def clients_get(cid):
folder = cid
path = output_folder / cid
if not path.exists():
abort(404)
info_file = path / 'info.txt'
info_text = info_file.read_text()
res = json.loads(info_text)
photo_glob = path.glob('*.jpg')
res['has_photos'] = bool(list(photo_glob))
print('GET client:', cid, 'res:', res)
return res
@app.route('/api/clients/<cid>/session', methods=['GET'])
def session_get(cid):
folder = cid
path = output_folder / cid
if not path.exists():
abort(404)
photo_glob = path.glob('*.jpg')
res = {}
res['photos'] = sorted([x.name for x in photo_glob])
return res
@app.route('/api/clients/<cid>/session', methods=['DELETE'])
def session_delete(cid):
folder = cid
path = output_folder / cid
if not path.exists():
abort(404)
photo_glob = path.glob('*.jpg')
for p in photo_glob:
p.unlink()
print('DELETE session:', cid)
return ''
@app.route('/api/clients/<cid>/session', methods=['POST'])
def session_post(cid):
content = request.json
light_time = content.get('light_time', 5000)
global status
print('POST session:', cid)
folder = cid
path = output_folder / cid
if not path.exists():
abort(404)
# go through the photo taking process
try:
# warmup
status = WARMUP
power.lights_on()
time.sleep(2)
power.lights_off()
time.sleep(1)
except BaseException as e:
print('Problem with lights: {} - {}'.format(e.__class__.__name__, str(e)))
print()
print('Are you sure the system is connected?')
print()
abort(500)
# capture
status = CAPTURING_PHOTO
power.lights_on()
time.sleep(0.1)
capture.trigger_capture()
time.sleep(light_time / 1000)
power.lights_off()
status = WRITING
time.sleep(max(5 - light_time / 1000, 1))
status = DOWNLOADING
download.download_all_photos(path)
time.sleep(3)
status = STANDBY
print('Finished.')
return ''
@app.route('/') @app.route('/')
def index(): def index():
return app.send_static_file('index.html') return app.send_static_file('index.html')
app.run() @app.errorhandler(404)
def not_found(e):
return app.send_static_file('index.html')
@app.route('/output/<path:filename>')
def output(filename):
return send_from_directory('output/', filename)
app.run(host='0.0.0.0')

0
server/output/.gitkeep Normal file
View File

View File

@@ -3,6 +3,8 @@ import settings
import time import time
def np_02B_api(ip, username, password, is_on): def np_02B_api(ip, username, password, is_on):
if settings.DEBUG: return
if is_on: if is_on:
endpoint = '/cmd.cgi?grp=0' endpoint = '/cmd.cgi?grp=0'
else: else:

View File

@@ -1,5 +1,36 @@
certifi==2020.12.5 appdirs==1.4.3
chardet==4.0.0 bcrypt==3.2.0
idna==2.10 CacheControl==0.12.6
requests==2.25.1 certifi==2019.11.28
urllib3==1.26.3 cffi==1.14.5
chardet==3.0.4
click==7.1.2
colorama==0.4.3
contextlib2==0.6.0
cryptography==3.4.6
distlib==0.3.0
distro==1.4.0
Flask==1.1.2
Flask-Cors==3.0.10
html5lib==1.0.1
idna==2.8
ipaddr==2.2.0
itsdangerous==1.1.0
Jinja2==2.11.3
lockfile==0.12.2
MarkupSafe==1.1.1
msgpack==0.6.2
packaging==20.3
paramiko==2.7.2
pep517==0.8.2
progress==1.5
pycparser==2.20
PyNaCl==1.4.0
pyparsing==2.4.6
pytoml==0.1.21
requests==2.22.0
retrying==1.3.3
six==1.14.0
urllib3==1.25.8
webencodings==0.5.1
Werkzeug==1.0.1

View File

@@ -1,3 +1,6 @@
import os
DEBUG = os.environ.get('FLASK_ENV', None) == 'development'
LIGHT_IP = '192.168.99.25' LIGHT_IP = '192.168.99.25'
LIGHT_USER = 'admin' LIGHT_USER = 'admin'
LIGHT_PASS = 'admin' LIGHT_PASS = 'admin'