Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
Jimmy Pellier | 045d1a54a3 | 5 years ago |
Jimmy Pellier | 085c544587 | 5 years ago |
Jimmy Pellier | 4cb28e49de | 5 years ago |
Jimmy Pellier | 04c5230b6d | 5 years ago |
Jimmy Pellier | be9a12a9d8 | 5 years ago |
Jimmy Pellier | 1a8b9302d9 | 5 years ago |
@ -0,0 +1,8 @@ |
|||||||
|
VUE_APP_BACK_URL=http://localhost/covid |
||||||
|
VUE_APP_BACK_USER=http://localhost/user |
||||||
|
VUE_APP_IMAGE_STORE=http://localhost/apache/public/uploads/ |
||||||
|
VUE_APP_TEMPERATURE_HUB=http://localhost/covid/batchExecutionHub |
||||||
|
VUE_APP_TIMEOUT_TEMPERATURE_HUB=5000 |
||||||
|
VUE_APP_MSG_TEMPERATURE_HUB=temperature |
||||||
|
VUE_APP_EMIT_MSG_TEMPERATURE=received-temperature |
||||||
|
VUE_APP_MAX_CHART=30 |
@ -0,0 +1,8 @@ |
|||||||
|
VUE_APP_BACK_URL=http://localhost/covid |
||||||
|
VUE_APP_BACK_USER=http://localhost/user |
||||||
|
VUE_APP_IMAGE_STORE=http://localhost/apache/public/uploads/ |
||||||
|
VUE_APP_TEMPERATURE_HUB=http://localhost/covid/batchExecutionHub |
||||||
|
VUE_APP_TIMEOUT_TEMPERATURE_HUB=5000 |
||||||
|
VUE_APP_MSG_TEMPERATURE_HUB=temperature |
||||||
|
VUE_APP_EMIT_MSG_TEMPERATURE=received-temperature |
||||||
|
VUE_APP_MAX_CHART=30 |
@ -0,0 +1,21 @@ |
|||||||
|
.DS_Store |
||||||
|
node_modules |
||||||
|
/dist |
||||||
|
|
||||||
|
# local env files |
||||||
|
.env.local |
||||||
|
.env.*.local |
||||||
|
|
||||||
|
# Log files |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.idea |
||||||
|
.vscode |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
*.sw? |
@ -0,0 +1,13 @@ |
|||||||
|
# étape de build |
||||||
|
FROM node:lts-alpine as build-stage |
||||||
|
WORKDIR /app |
||||||
|
COPY package*.json ./ |
||||||
|
RUN npm install |
||||||
|
COPY . . |
||||||
|
RUN npm run build |
||||||
|
|
||||||
|
# étape de production |
||||||
|
FROM nginx:stable-alpine as production-stage |
||||||
|
COPY --from=build-stage /app/dist /usr/share/nginx/html |
||||||
|
EXPOSE 80 |
||||||
|
CMD ["nginx", "-g", "daemon off;"] |
@ -1,2 +1,29 @@ |
|||||||
# TemperatureCheckerFront |
# clientapp |
||||||
|
|
||||||
|
## Project setup |
||||||
|
``` |
||||||
|
npm install |
||||||
|
``` |
||||||
|
|
||||||
|
### Compiles and hot-reloads for development |
||||||
|
``` |
||||||
|
npm run serve |
||||||
|
``` |
||||||
|
|
||||||
|
### Compiles and minifies for production |
||||||
|
``` |
||||||
|
npm run build |
||||||
|
``` |
||||||
|
|
||||||
|
### Run your tests |
||||||
|
``` |
||||||
|
npm run test |
||||||
|
``` |
||||||
|
|
||||||
|
### Lints and fixes files |
||||||
|
``` |
||||||
|
npm run lint |
||||||
|
``` |
||||||
|
|
||||||
|
### Customize configuration |
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/). |
||||||
|
@ -0,0 +1,6 @@ |
|||||||
|
module.exports = { |
||||||
|
presets: [ |
||||||
|
'@vue/cli-plugin-babel/preset' |
||||||
|
], |
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
.corps { |
||||||
|
height: 80vh; |
||||||
|
display: block; |
||||||
|
position: relative; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.bg-passageOk |
||||||
|
{ |
||||||
|
background-color: seagreen; |
||||||
|
} |
||||||
|
|
||||||
|
.corps::after { |
||||||
|
content: ""; |
||||||
|
background-repeat:no-repeat; |
||||||
|
background-position:center center; |
||||||
|
background-attachment:fixed; |
||||||
|
background-size: 50vh auto; |
||||||
|
background-image: url("../src/assets/logo.png"); |
||||||
|
opacity: 0.5; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
bottom: 0; |
||||||
|
right: 0; |
||||||
|
position: absolute; |
||||||
|
z-index: -1; |
||||||
|
} |
||||||
|
|
||||||
|
.fade-enter-active{ |
||||||
|
transition: opacity .5s; |
||||||
|
} |
||||||
|
.fade-enter /* .fade-leave-active below version 2.1.8 */ { |
||||||
|
opacity: 0; |
||||||
|
} |
@ -0,0 +1,296 @@ |
|||||||
|
@charset "UTF-8"; |
||||||
|
@import url("https://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600"); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
body { |
||||||
|
background-color: #FFF; |
||||||
|
margin: 0px; |
||||||
|
font-family: Ubuntu, sans-serif; |
||||||
|
color: #1e1e1e; |
||||||
|
font-weight: normal; |
||||||
|
padding-top: 0; |
||||||
|
} |
||||||
|
|
||||||
|
h1, h2, h3, h4 { |
||||||
|
font-family: "Cabin Condensed", sans-serif; |
||||||
|
} |
||||||
|
|
||||||
|
header { |
||||||
|
padding: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
header .headline { |
||||||
|
max-width: 640px; |
||||||
|
margin: 0 auto; |
||||||
|
} |
||||||
|
|
||||||
|
header .headline h1 { |
||||||
|
font-size: 3em; |
||||||
|
margin-bottom: 0; |
||||||
|
} |
||||||
|
|
||||||
|
header .headline h2 { |
||||||
|
margin-top: 0.2em; |
||||||
|
} |
||||||
|
|
||||||
|
footer { |
||||||
|
padding: 1em 2em 2em; |
||||||
|
} |
||||||
|
|
||||||
|
#container { |
||||||
|
width: 640px; |
||||||
|
margin: 20px auto; |
||||||
|
padding: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
#interactive.viewport { |
||||||
|
width: 640px; |
||||||
|
height: 480px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#interactive.viewport canvas, video { |
||||||
|
float: left; |
||||||
|
width: 640px; |
||||||
|
height: 480px; |
||||||
|
} |
||||||
|
|
||||||
|
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer { |
||||||
|
margin-left: -640px; |
||||||
|
} |
||||||
|
|
||||||
|
.controls fieldset { |
||||||
|
border: none; |
||||||
|
margin: 0; |
||||||
|
padding: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.controls .input-group { |
||||||
|
float: left; |
||||||
|
} |
||||||
|
|
||||||
|
.controls .input-group input, .controls .input-group button { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
.controls .reader-config-group { |
||||||
|
float: right; |
||||||
|
} |
||||||
|
|
||||||
|
.controls .reader-config-group label { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
.controls .reader-config-group label span { |
||||||
|
width: 9rem; |
||||||
|
display: inline-block; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
.controls:after { |
||||||
|
content: ''; |
||||||
|
display: block; |
||||||
|
clear: both; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#result_strip { |
||||||
|
margin: 10px 0; |
||||||
|
border-top: 1px solid #EEE; |
||||||
|
border-bottom: 1px solid #EEE; |
||||||
|
padding: 10px 0; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul { |
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
list-style-type: none; |
||||||
|
width: auto; |
||||||
|
overflow-x: auto; |
||||||
|
overflow-y: hidden; |
||||||
|
white-space: nowrap; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul > li { |
||||||
|
display: inline-block; |
||||||
|
vertical-align: middle; |
||||||
|
width: 160px; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul > li .thumbnail { |
||||||
|
padding: 5px; |
||||||
|
margin: 4px; |
||||||
|
border: 1px dashed #CCC; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul > li .thumbnail img { |
||||||
|
max-width: 140px; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul > li .thumbnail .caption { |
||||||
|
white-space: normal; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul > li .thumbnail .caption h4 { |
||||||
|
text-align: center; |
||||||
|
word-wrap: break-word; |
||||||
|
height: 40px; |
||||||
|
margin: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip > ul:after { |
||||||
|
content: ""; |
||||||
|
display: table; |
||||||
|
clear: both; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.scanner-overlay { |
||||||
|
display: none; |
||||||
|
width: 640px; |
||||||
|
height: 510px; |
||||||
|
position: absolute; |
||||||
|
padding: 20px; |
||||||
|
top: 50%; |
||||||
|
margin-top: -275px; |
||||||
|
left: 50%; |
||||||
|
margin-left: -340px; |
||||||
|
background-color: #FFF; |
||||||
|
-moz-box-shadow: #333333 0px 4px 10px; |
||||||
|
-webkit-box-shadow: #333333 0px 4px 10px; |
||||||
|
box-shadow: #333333 0px 4px 10px; |
||||||
|
} |
||||||
|
|
||||||
|
.scanner-overlay > .header { |
||||||
|
position: relative; |
||||||
|
margin-bottom: 14px; |
||||||
|
} |
||||||
|
|
||||||
|
.scanner-overlay > .header h4, .scanner-overlay > .header .close { |
||||||
|
line-height: 16px; |
||||||
|
} |
||||||
|
|
||||||
|
.scanner-overlay > .header h4 { |
||||||
|
margin: 0px; |
||||||
|
padding: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
.scanner-overlay > .header .close { |
||||||
|
position: absolute; |
||||||
|
right: 0px; |
||||||
|
top: 0px; |
||||||
|
height: 16px; |
||||||
|
width: 16px; |
||||||
|
text-align: center; |
||||||
|
font-weight: bold; |
||||||
|
font-size: 14px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
i.icon-24-scan { |
||||||
|
width: 24px; |
||||||
|
height: 24px; |
||||||
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzFFMjMzNTBFNjcwMTFFMkIzMERGOUMzMzEzM0E1QUMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzFFMjMzNTFFNjcwMTFFMkIzMERGOUMzMzEzM0E1QUMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMUUyMzM0RUU2NzAxMUUyQjMwREY5QzMzMTMzQTVBQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMUUyMzM0RkU2NzAxMUUyQjMwREY5QzMzMTMzQTVBQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtQr90wAAAUuSURBVHjanFVLbFRVGP7ua97T9DGPthbamAYYBNSMVbBpjCliWWGIEBMWsnDJxkh8RDeEDW5MDGticMmGBWnSlRSCwgLFNkqmmrRIqzjTznTazkxn5s7c6/efzm0G0Jhwkj/nP+d/nv91tIWFBTQaDQWapkGW67p4ltUub5qmAi0UCqF/a/U2m81tpmddotwwDGSz2dzi4uKSaOucnJycGhsbe1XXdQiIIcdxEAgEtgXq9brySHCht79UXi/8QheawN27d385fPjwuEl6XyKR6LdtW7t06RLK5TKOHj2K/fv3Q87Dw8OYn5/HiRMnMDs7i5mZGQwODiqlPp8PuVwO6XRaOXb16lXl1OnTp5FMJvtosF8M+MWLarWqGJaWlpBKpRRcu3YN4+PjmJ6exsTEhDJw5coVjI6OKgPhcBiZTAbxeBx+vx+XL19Gd3c3Tp48Ka9zqDYgBlTQxYNgMIhIJKLCILkQb+TZsgvdsiyFi+feWRR7oRNZyanQtvW2V4DEUUBiK2eJpeDirSyhCe7F2QPh8fiEp72i9PbsC5G52DbiKZA771yr1dTuGfJ4PQNPFoAyQNR1aNEmsS5eyB3PgjeooMZd2AWvNmzYci/Gea7TeFOcI93jV/K67noGmi4vdRI9gPSDeMLSdKUBZZczlWm1rTtHjLZ24d+WER2tc8N1m+Y+ID74wx0zGYvhg9UNrJdtHJyZRdQfwPsrq9g99xsGlgsYmr6BNzO/IVwsYfjBQ6XYz6JI/72MV366B5/lw0elOkJWGUM3bmKtWjXSLuLaBWhnPnnp0FfoiFi4+TMfVAb2poBkDLjO845uYLEAjL4ALGWBP5YAOsP4AJYBFDaB1HOSVWD2PuV95H2RdV93Lv74/cf6p6Zxq/h6OofeOPJBC39JtONdwOAAViOs4p4OFGTf0Uc8iiyrr9YdQrUnDLsngrVOC0jQib44HlF2RafRZBz1Qy+vfhgK3NJZBlrm+LEm9qWwzFgLU7Ozg0JxZP06jQSRpQ7EerAWDSt6PuhHPmChEAog56fCLvJT5hHTm3OZkz3DyLx7XNWTGEA1GkV14gjWgwbW0ESVjYRwCOuai03L5E7OUBAV4kXSS4auoGIaKOma4m8EA5R1sMEGLh95C+XuLph0WJWpxepYYLtfT0RRgY1KgNODY6BoaChRuEhDCIZQYseuki5KN6hcQHiq7OZNv4/Zq2O6P4Lfkwn46vZjjaYZrIpvWbpzjLErrc4xUGE4avRedpYJalRcIl5hQius/SrPm9xrNOQYJhao6BvNUeWqtY8KaWuNjHOFAr7mM9f4NA4UbKysoUJ8PV9UzVOx6wxDDWUOxnK1pmCD07fOMAvtIsM3l89Dl3HRGhVma9AZMqjOnz2LQqWCxs6dqr3T7x1DTzKJaG8SekcHhg4cgI/56uKdlKnBV/WndqN3YAB/7tyBd3oT6GBIOzs7kc/nDfFdDFT5bS73cp06dQoaPa/Rw/rtO/resTHxxE2m9rCrbSR27UJCcMf1BpiA5rAAGgdfc868fUR1sMwj0cm9Iu9IctweisViB3hhKTHDcHc5jv/LspbyaZrR1OD82/fIlOkuB9LnEWRmDX2TsddUPg3D5gvuc0je0rZaD5EW6G3yjS+A3eeBEWq3XW/Abw1HhUspXADufQb86oW7tZytkYCN//3hHwBvDALPi8EnSOYK8DAOfCc2h4aGcO7cuafkzampqf9UripH12/DtOZbx8ciVGzYy5OO40o25ascGRl5Ssc/AgwAjW3JwqIUjSYAAAAASUVORK5CYII="); |
||||||
|
display: inline-block; |
||||||
|
background-repeat: no-repeat; |
||||||
|
line-height: 24px; |
||||||
|
margin-top: 1px; |
||||||
|
vertical-align: text-top; |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 603px) { |
||||||
|
|
||||||
|
#container { |
||||||
|
width: 300px; |
||||||
|
margin: 10px auto; |
||||||
|
-moz-box-shadow: none; |
||||||
|
-webkit-box-shadow: none; |
||||||
|
box-shadow: none; |
||||||
|
} |
||||||
|
|
||||||
|
#container form.voucher-form input.voucher-code { |
||||||
|
width: 180px; |
||||||
|
} |
||||||
|
} |
||||||
|
@media (max-width: 603px) { |
||||||
|
|
||||||
|
.reader-config-group { |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.reader-config-group label > span { |
||||||
|
width: 50%; |
||||||
|
} |
||||||
|
|
||||||
|
.reader-config-group label > select, .reader-config-group label > input { |
||||||
|
max-width: calc(50% - 2px); |
||||||
|
} |
||||||
|
|
||||||
|
#interactive.viewport { |
||||||
|
width: 300px; |
||||||
|
height: 300px; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#interactive.viewport canvas, video { |
||||||
|
margin-top: -50px; |
||||||
|
width: 300px; |
||||||
|
height: 400px; |
||||||
|
} |
||||||
|
|
||||||
|
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer { |
||||||
|
margin-left: -300px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#result_strip { |
||||||
|
margin-top: 5px; |
||||||
|
padding-top: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip ul.thumbnails > li { |
||||||
|
width: 150px; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip ul.thumbnails > li .thumbnail .imgWrapper { |
||||||
|
width: 130px; |
||||||
|
height: 130px; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
#result_strip ul.thumbnails > li .thumbnail .imgWrapper img { |
||||||
|
margin-top: -25px; |
||||||
|
width: 130px; |
||||||
|
height: 180px; |
||||||
|
} |
||||||
|
} |
||||||
|
@media (max-width: 603px) { |
||||||
|
|
||||||
|
.overlay.scanner { |
||||||
|
width: 640px; |
||||||
|
height: 510px; |
||||||
|
padding: 20px; |
||||||
|
margin-top: -275px; |
||||||
|
margin-left: -340px; |
||||||
|
background-color: #FFF; |
||||||
|
-moz-box-shadow: none; |
||||||
|
-webkit-box-shadow: none; |
||||||
|
box-shadow: none; |
||||||
|
} |
||||||
|
|
||||||
|
.overlay.scanner > .header { |
||||||
|
margin-bottom: 14px; |
||||||
|
} |
||||||
|
|
||||||
|
.overlay.scanner > .header h4, .overlay.scanner > .header .close { |
||||||
|
line-height: 16px; |
||||||
|
} |
||||||
|
|
||||||
|
.overlay.scanner > .header .close { |
||||||
|
height: 16px; |
||||||
|
width: 16px; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
tempfront |
@ -0,0 +1,64 @@ |
|||||||
|
{ |
||||||
|
"name": "coviiiiid", |
||||||
|
"version": "0.1.0", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"serve": "node_modules/.bin/vue-cli-service serve", |
||||||
|
"build": "node_modules/.bin/vue-cli-service build", |
||||||
|
"lint": "node_modules/.bin/vue-cli-service lint" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@aspnet/signalr": "^1.1.4", |
||||||
|
"@fortawesome/fontawesome-svg-core": "^1.2.28", |
||||||
|
"@fortawesome/free-solid-svg-icons": "^5.13.0", |
||||||
|
"@fortawesome/vue-fontawesome": "^0.1.9", |
||||||
|
"axios": "^0.19.2", |
||||||
|
"bootstrap-vue": "^2.15.0", |
||||||
|
"chart.js": "^2.9.3", |
||||||
|
"commander": "^4.0.1", |
||||||
|
"core-js": "^3.4.3", |
||||||
|
"debounce": "^1.2.0", |
||||||
|
"es6-promise": "^4.2.8", |
||||||
|
"jquery": "^1.9.1", |
||||||
|
"keycloak-js": "^10.0.2", |
||||||
|
"nimble": "0.0.2", |
||||||
|
"node-datetime": "^2.1.2", |
||||||
|
"request-digest": "^1.0.13", |
||||||
|
"socket.io": "^2.3.0", |
||||||
|
"vue": "^2.6.10", |
||||||
|
"vue-chartjs": "^3.5.0", |
||||||
|
"vue-i18n": "^8.18.1", |
||||||
|
"vue-infinite-loading": "^2.4.5", |
||||||
|
"vue-js-toggle-button": "^1.3.3", |
||||||
|
"vue-router": "^3.3.2", |
||||||
|
"vuex": "^3.4.0", |
||||||
|
"xml2js": "^0.4.22" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@vue/cli-plugin-babel": "^4.1.0", |
||||||
|
"@vue/cli-plugin-eslint": "^4.1.0", |
||||||
|
"@vue/cli-service": "^4.1.0", |
||||||
|
"babel-eslint": "^10.0.3", |
||||||
|
"eslint": "^5.16.0", |
||||||
|
"eslint-plugin-vue": "^5.0.0", |
||||||
|
"vue-template-compiler": "^2.6.10" |
||||||
|
}, |
||||||
|
"eslintConfig": { |
||||||
|
"root": true, |
||||||
|
"env": { |
||||||
|
"node": true |
||||||
|
}, |
||||||
|
"extends": [ |
||||||
|
"plugin:vue/essential", |
||||||
|
"eslint:recommended" |
||||||
|
], |
||||||
|
"rules": {}, |
||||||
|
"parserOptions": { |
||||||
|
"parser": "babel-eslint" |
||||||
|
} |
||||||
|
}, |
||||||
|
"browserslist": [ |
||||||
|
"> 1%", |
||||||
|
"last 2 versions" |
||||||
|
] |
||||||
|
} |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,17 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> |
||||||
|
<title>covid finder</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<noscript> |
||||||
|
<strong>We're sorry but clientapp doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> |
||||||
|
</noscript> |
||||||
|
<div id="app"></div> |
||||||
|
<!-- built files will be auto injected --> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,10 @@ |
|||||||
|
{ |
||||||
|
"realm": "Test", |
||||||
|
"auth-server-url": "https://oauth.apsidetop-devel.net/auth", |
||||||
|
"ssl-required": "external", |
||||||
|
"resource": "test", |
||||||
|
"credentials": { |
||||||
|
"secret": "5cf010f0-b4a9-4db9-a9f2-102bc703c448" |
||||||
|
}, |
||||||
|
"confidential-port": 0 |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
<template> |
||||||
|
<b-container fluid id="app"> |
||||||
|
<Menu class="header" /> |
||||||
|
<div style="margin-top:60px"> |
||||||
|
<router-view > </router-view> |
||||||
|
</div> |
||||||
|
</b-container> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import Menu from './components/Menu.vue' |
||||||
|
import Vue from 'vue' |
||||||
|
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' |
||||||
|
import 'bootstrap/dist/css/bootstrap.css' |
||||||
|
import 'bootstrap-vue/dist/bootstrap-vue.css' |
||||||
|
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core' |
||||||
|
import { faUserSecret } from '@fortawesome/free-solid-svg-icons' |
||||||
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' |
||||||
|
|
||||||
|
library.add(faUserSecret) |
||||||
|
|
||||||
|
Vue.component('font-awesome-icon', FontAwesomeIcon) |
||||||
|
|
||||||
|
// Install BootstrapVue |
||||||
|
Vue.use(BootstrapVue) |
||||||
|
// Optionally install the BootstrapVue icon components plugin |
||||||
|
Vue.use(IconsPlugin) |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'app', |
||||||
|
components: { |
||||||
|
Menu |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
#app { |
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif; |
||||||
|
-webkit-font-smoothing: antialiased; |
||||||
|
-moz-osx-font-smoothing: grayscale; |
||||||
|
text-align: center; |
||||||
|
color: #2c3e50; |
||||||
|
margin-top: 0px; |
||||||
|
padding-left: 0; |
||||||
|
padding-right: 0 |
||||||
|
} |
||||||
|
.header { |
||||||
|
position:fixed; /* fixing the position takes it out of html flow - knows |
||||||
|
nothing about where to locate itself except by browser |
||||||
|
coordinates */ |
||||||
|
left:0; /* top left corner should start at leftmost spot */ |
||||||
|
top:0; /* top left corner should start at topmost spot */ |
||||||
|
width:100vw; /* take up the full browser width */ |
||||||
|
z-index:100; /* high z index so other content scrolls underneath */ |
||||||
|
height:50px; /* define height for content */ |
||||||
|
} |
||||||
|
</style> |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,91 @@ |
|||||||
|
<template> |
||||||
|
<div id="videoapp"> |
||||||
|
<b-button v-b-modal.modal-2 > |
||||||
|
<font-awesome-icon icon="camera" /> |
||||||
|
{{$t('user_detail_photo')}} |
||||||
|
</b-button> |
||||||
|
<b-modal static size="xl" id="modal-2" :hide-footer="true" > |
||||||
|
<template v-slot:modal-header> |
||||||
|
Title |
||||||
|
<b-button size="sm" variant="danger" @click="close()"> |
||||||
|
Cancel |
||||||
|
</b-button> |
||||||
|
|
||||||
|
</template> |
||||||
|
<template v-slot:default> |
||||||
|
<div> |
||||||
|
<div><video ref="video" id="video" width="640" height="480" autoplay ></video></div> |
||||||
|
<div><button id="snap" v-on:click="capture()">Snap Photo</button></div> |
||||||
|
<canvas ref="canvas" id="canvas" width="640" height="480"></canvas> |
||||||
|
<ul> |
||||||
|
<li v-for="c in captures" v-bind:key="c.id"> |
||||||
|
<img v-bind:src="c.photo" alt="Photo" height="50" /> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
</b-modal> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: "CapturePhoto", |
||||||
|
mounted() { |
||||||
|
this.video = this.$refs.video; |
||||||
|
let video = document.querySelector('video'); |
||||||
|
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { |
||||||
|
navigator.mediaDevices.getUserMedia({ video: true }).then(stream => { |
||||||
|
video.srcObject = stream |
||||||
|
}); |
||||||
|
} |
||||||
|
}, |
||||||
|
data () { |
||||||
|
return { |
||||||
|
video: {}, |
||||||
|
canvas: {}, |
||||||
|
compteur:0, |
||||||
|
captures: [], |
||||||
|
snapshot:{} |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
close() |
||||||
|
{ |
||||||
|
this.snapshot= null; |
||||||
|
this.$bvModal.hide("modal-2"); |
||||||
|
}, |
||||||
|
capture() { |
||||||
|
this.canvas = this.$refs.canvas; |
||||||
|
this.canvas.getContext("2d").drawImage(this.video, 0, 0, 640, 480); |
||||||
|
|
||||||
|
this.compteur++; |
||||||
|
this.snapshot = {photo:this.canvas.toDataURL("image/png"), id:this.compteur++} |
||||||
|
// this.captures.push(this.snapshot); |
||||||
|
this.$emit('update:snapshot', this.snapshot); |
||||||
|
this.$bvModal.hide("modal-2"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
body: { |
||||||
|
background-color: #F0F0F0; |
||||||
|
} |
||||||
|
/* #app { |
||||||
|
text-align: center; |
||||||
|
color: #2c3e50; |
||||||
|
margin-top: 60px; |
||||||
|
}*/ |
||||||
|
#video { |
||||||
|
background-color: #000000; |
||||||
|
} |
||||||
|
#canvas { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
li { |
||||||
|
display: inline; |
||||||
|
padding: 5px; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,160 @@ |
|||||||
|
<template> |
||||||
|
<div class="MenuNavBar"> |
||||||
|
<b-navbar toggleable="lg" type="dark" variant="info" style="min-height:60px; padding: 0px 0px 0px 0px"> |
||||||
|
|
||||||
|
<b-navbar-brand class="nav-link d-flex align-items-center justify-content-center" @click="doLogin"> |
||||||
|
<b-avatar variant="info" v-bind:src="img" size="3rem" > |
||||||
|
</b-avatar> |
||||||
|
</b-navbar-brand> |
||||||
|
|
||||||
|
<b-navbar-brand class="nav-link d-flex align-items-center justify-content-center" to="/" > |
||||||
|
{{$t('title')}} |
||||||
|
</b-navbar-brand> |
||||||
|
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle> |
||||||
|
<b-collapse id="nav-collapse" is-nav> |
||||||
|
<b-navbar-nav> |
||||||
|
<b-nav-item v-if="isAdmin" active-class="active" class="nav-link d-flex align-items-center justify-content-center" to="/users" >{{$t('user_title')}}</b-nav-item> |
||||||
|
<b-nav-item active-class="active" class="nav-link d-flex align-items-center justify-content-center" @click="logout" >{{$t('logout')}}</b-nav-item> |
||||||
|
</b-navbar-nav> |
||||||
|
|
||||||
|
</b-collapse> |
||||||
|
|
||||||
|
</b-navbar> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
|
||||||
|
import { BNavbar, BNavbarBrand, BNavbarToggle } from 'bootstrap-vue' |
||||||
|
import { BAvatar } from 'bootstrap-vue' |
||||||
|
import UserService from "../services/UserService"; |
||||||
|
import Vue from "vue"; |
||||||
|
import axios from "axios"; |
||||||
|
|
||||||
|
Vue.component('b-avatar', BAvatar) |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "Menu", |
||||||
|
components : |
||||||
|
{ |
||||||
|
BNavbar,BNavbarBrand, BNavbarToggle, BAvatar |
||||||
|
}, |
||||||
|
data:function(){ |
||||||
|
return { |
||||||
|
} |
||||||
|
}, |
||||||
|
computed : |
||||||
|
{ |
||||||
|
user(){ |
||||||
|
return this.$store.state.user; |
||||||
|
}, |
||||||
|
img() |
||||||
|
{ |
||||||
|
return this.$store.state.user != null && this.$store.state.user.image != null ? |
||||||
|
process.env.VUE_APP_IMAGE_STORE + this.$store.state.user.image : |
||||||
|
''; |
||||||
|
}, |
||||||
|
isAdmin() { |
||||||
|
return this.$store.state.roles.find(l => l.name == 'Administrators' && l.isActived) != null; |
||||||
|
}, |
||||||
|
isClient() { |
||||||
|
return this.$store.state.roles.find(l => l.name == 'customer' && l.isActived) != null; |
||||||
|
}, |
||||||
|
isLivreur() { |
||||||
|
return this.$store.state.roles.find(l => l.name == 'delivery' && l.isActived) != null; |
||||||
|
}, |
||||||
|
isManager() { |
||||||
|
return this.$store.state.roles.find(l => l.name == 'manager' && l.isActived) != null; |
||||||
|
}, |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
let that = this; |
||||||
|
UserService.initKeycloak( function () { |
||||||
|
sessionStorage.setItem("react-token", UserService.getToken()); |
||||||
|
sessionStorage.setItem("react-refresh-token", UserService.getRefreshToken()); |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers: { |
||||||
|
'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/users/bykeycloakid?guid=" + UserService.getUsername().sub, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
if( result.data.keycloakId !== null) |
||||||
|
that.$store.commit("updateUser", result.data); |
||||||
|
else |
||||||
|
UserService.doLogout(); |
||||||
|
}); |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/roles/bykeycloakid?keycloakid=" + UserService.getUsername().sub, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
that.$store.commit("updateRole", result.data); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
function(){ |
||||||
|
that.$store.commit("updateUser", null); |
||||||
|
that.$store.commit("updateRole", []); |
||||||
|
if(that.$router.currentRoute.path !== "/") |
||||||
|
that.$router.push({path:"/"}); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
methods : { |
||||||
|
logout: function () |
||||||
|
{ |
||||||
|
UserService.doLogout(); |
||||||
|
sessionStorage.setItem("react-token", ""); |
||||||
|
sessionStorage.setItem("react-refresh-token", ""); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
doLogin : function() |
||||||
|
{ |
||||||
|
|
||||||
|
let that = this; |
||||||
|
UserService.initKeycloak( function () { |
||||||
|
|
||||||
|
sessionStorage.setItem("react-token", UserService.getToken()); |
||||||
|
sessionStorage.setItem("react-refresh-token", UserService.getRefreshToken()); |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers: { |
||||||
|
'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/users/bykeycloakid?guid=" + UserService.getUsername().sub, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
if( result.data.keycloakId !== null) |
||||||
|
that.$store.commit("updateUser", result.data); |
||||||
|
else |
||||||
|
UserService.doLogout(); |
||||||
|
}); |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/roles/bykeycloakid?keycloakid=" + UserService.getUsername().sub, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
that.$store.commit("updateRole", result.data); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
UserService.doLogin()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,271 @@ |
|||||||
|
<template> |
||||||
|
<b-container fluid id="temperature"> |
||||||
|
<b-row style="padding-top: 10px"> |
||||||
|
<b-col> |
||||||
|
<b-avatar |
||||||
|
v-if="!acquirementInprogress" |
||||||
|
v-b-tooltip.hover :title="$t('sub_title')" |
||||||
|
variant="info" :src="image_avatar" |
||||||
|
size="10rem" style="margin-right: 5px" |
||||||
|
> |
||||||
|
</b-avatar> |
||||||
|
|
||||||
|
<div><video |
||||||
|
v-if="acquirementInprogress" |
||||||
|
ref="video" |
||||||
|
id="video" width="160" height="160" style="border-radius: 80px;" |
||||||
|
:srcObject.prop="stream" |
||||||
|
autoplay /> |
||||||
|
</div> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row> |
||||||
|
|
||||||
|
</b-row> |
||||||
|
<b-row> |
||||||
|
<b-col> |
||||||
|
<label style="font-size: 10px;font-style: italic">{{message}}</label> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row v-if="result != null"> |
||||||
|
<b-col> |
||||||
|
{{result.temperature | formatFloat}} {{$t('temperature_unit')}} |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row style="padding-top: 10px"> |
||||||
|
<b-col cols="6" class="d-flex justify-content-start"> |
||||||
|
{{$t('object_temperature')}} : {{received.Element | formatFloat}} {{$t('temperature_unit')}} |
||||||
|
</b-col> |
||||||
|
<b-col cols="6" class="d-flex justify-content-start"> |
||||||
|
{{$t('ambient_temperature')}} : {{received.Ambient | formatFloat}} {{$t('temperature_unit')}} |
||||||
|
</b-col> |
||||||
|
<b-col cols="12" class="d-flex justify-content-start" v-if="isAdmin"> |
||||||
|
{{$t('emissivity')}} : {{received.Emissivity | formatFloat}} |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row v-if="isAuth"> |
||||||
|
<test-chart |
||||||
|
:options="options" |
||||||
|
:styles="myStyles" |
||||||
|
:chart-data="datacollection"></test-chart> |
||||||
|
</b-row> |
||||||
|
<b-row> |
||||||
|
<b-col> |
||||||
|
<b-button @click="acquireTemp" variant="danger" :disabled="acquirementInprogress" v-if="isAuth"> |
||||||
|
{{$t('temperature_acq')}} |
||||||
|
<font-awesome-icon icon="question" /> |
||||||
|
|
||||||
|
<b-spinner v-if="acquirementInprogress" type="border" :label="$t('inprogress')"/> |
||||||
|
</b-button> |
||||||
|
</b-col> |
||||||
|
|
||||||
|
</b-row> |
||||||
|
|
||||||
|
</b-container> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import axios from "axios"; |
||||||
|
import TestChart from "./TestChart"; |
||||||
|
import Vue from "vue"; |
||||||
|
import '../services/TemperatureHub.js' |
||||||
|
import TemperatureHub from "../services/TemperatureHub"; |
||||||
|
|
||||||
|
Vue.use(TestChart); |
||||||
|
|
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'Temperature', |
||||||
|
components: { |
||||||
|
TestChart |
||||||
|
}, |
||||||
|
|
||||||
|
props: { |
||||||
|
msg: String |
||||||
|
}, |
||||||
|
data:function(){ |
||||||
|
return { |
||||||
|
stream:{}, |
||||||
|
datacollection: {}, |
||||||
|
received : {}, |
||||||
|
|
||||||
|
acquirementInprogress:false, |
||||||
|
result: null, |
||||||
|
x:'', |
||||||
|
options: { |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
}, |
||||||
|
roles:[], |
||||||
|
temperatureHub : null |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
image() |
||||||
|
{ |
||||||
|
return this.$store.state.user != null && this.$store.state.user.image != null ? |
||||||
|
process.env.VUE_APP_IMAGE_STORE + this.$store.state.user.image : |
||||||
|
require('@/assets/coviiiiiid.jpg'); |
||||||
|
}, |
||||||
|
myStyles() { |
||||||
|
return { |
||||||
|
height: `300px`, |
||||||
|
width:'100%', |
||||||
|
position: 'relative' |
||||||
|
} |
||||||
|
}, |
||||||
|
message() |
||||||
|
{ |
||||||
|
if(this.result == null) |
||||||
|
return ""; |
||||||
|
else |
||||||
|
return this.result.message; |
||||||
|
}, |
||||||
|
image_avatar() |
||||||
|
{ |
||||||
|
if(this.result == null) |
||||||
|
{ |
||||||
|
if(this.acquirementInprogress) |
||||||
|
return require('@/assets/waiting.jpg'); |
||||||
|
else |
||||||
|
return this.image; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
|
||||||
|
if(this.result.fever === null) |
||||||
|
return require('@/assets/out_of_range.jpg'); |
||||||
|
else if(this.result.fever) |
||||||
|
return require('@/assets/fever_ok.jpg'); |
||||||
|
else |
||||||
|
return this.image; |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
isAdmin() { |
||||||
|
|
||||||
|
return this.$store.state.roles.find(l => l.name == 'Administrators' && l.isActived) != null; |
||||||
|
}, |
||||||
|
isAuth() { |
||||||
|
|
||||||
|
return this.$store.state.roles !== undefined && this.$store.state.roles.find(l => l.isActived) != null; |
||||||
|
}, |
||||||
|
}, |
||||||
|
filters: { |
||||||
|
formatFloat : function(value) { |
||||||
|
if (value) { |
||||||
|
return parseFloat(value).toFixed(2); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
created () { |
||||||
|
this.temperatureHub = new TemperatureHub(); |
||||||
|
Vue.prototype.$questionHub = this.temperatureHub.startHub(); |
||||||
|
this.$questionHub.$on(process.env.VUE_APP_EMIT_MSG_TEMPERATURE, this.onChanged); |
||||||
|
|
||||||
|
// Listen to score changes coming from SignalR events |
||||||
|
|
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
|
||||||
|
this.fillData (); |
||||||
|
|
||||||
|
}, |
||||||
|
methods : { |
||||||
|
fillData () { |
||||||
|
this.datacollection = { |
||||||
|
labels: [], |
||||||
|
datasets: [ |
||||||
|
{ |
||||||
|
label: this.$t('temperature_chart_label'), |
||||||
|
backgroundColor: '#f87979', |
||||||
|
data: [] |
||||||
|
}] |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
onChanged: function(msg) { |
||||||
|
this.received = msg; |
||||||
|
|
||||||
|
let datacollection = { |
||||||
|
labels: [], |
||||||
|
datasets: [ |
||||||
|
{ |
||||||
|
label: this.$t('temperature_chart_label'), |
||||||
|
backgroundColor: '#f87979', |
||||||
|
data: [] |
||||||
|
}] |
||||||
|
} |
||||||
|
|
||||||
|
if(this.datacollection.labels.length > process.env.VUE_APP_MAX_CHART) |
||||||
|
{ |
||||||
|
this.datacollection.labels.shift(); |
||||||
|
this.datacollection.datasets[0].data.shift(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
datacollection.labels.push(...this.datacollection.labels); |
||||||
|
datacollection.datasets[0].data.push(...this.datacollection.datasets[0].data); |
||||||
|
|
||||||
|
datacollection.labels.push(this.x); |
||||||
|
datacollection.datasets[0].data.push(this.received.Element); |
||||||
|
this.datacollection = datacollection; |
||||||
|
|
||||||
|
}, |
||||||
|
acquireTemp : async function() |
||||||
|
{ |
||||||
|
try { |
||||||
|
this.acquirementInprogress = true; |
||||||
|
let that = this; |
||||||
|
this.video = this.$refs.video; |
||||||
|
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { |
||||||
|
navigator.mediaDevices.getUserMedia({ video: true }).then(stream => { |
||||||
|
that.stream = stream |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
this.result = null; |
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers:{'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
let result = await axios.get(process.env.VUE_APP_BACK_URL + "/api/temperature", axiosConfig); |
||||||
|
window.console.log(result); |
||||||
|
this.result = result.data; |
||||||
|
|
||||||
|
} |
||||||
|
finally { |
||||||
|
this.acquirementInprogress = false; |
||||||
|
this.stream = null; |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
beforeDestroy : async function() { |
||||||
|
|
||||||
|
// Make sure to cleanup SignalR event handlers when removing the component |
||||||
|
this.$questionHub.$off(process.env.VUE_APP_EMIT_MSG_TEMPERATURE, this.onChanged); |
||||||
|
await this.temperatureHub.stopHub(); |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only --> |
||||||
|
<style scoped> |
||||||
|
h3 { |
||||||
|
margin: 40px 0 0; |
||||||
|
} |
||||||
|
ul { |
||||||
|
list-style-type: none; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
li { |
||||||
|
display: inline-block; |
||||||
|
margin: 0 10px; |
||||||
|
} |
||||||
|
a { |
||||||
|
color: #42b983; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,17 @@ |
|||||||
|
import { Line, mixins } from 'vue-chartjs' |
||||||
|
const { reactiveProp } = mixins |
||||||
|
|
||||||
|
export default { |
||||||
|
extends: Line, |
||||||
|
mixins: [reactiveProp], |
||||||
|
props: ['options'], |
||||||
|
options: { |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
}, |
||||||
|
mounted () { |
||||||
|
// this.chartData is created in the mixin.
|
||||||
|
// If you want to pass options please create a local options object
|
||||||
|
this.renderChart(this.chartData, this.options) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<b-card v-if="user.isNew == undefined" no-body class="overflow-hidden cardUser" |
||||||
|
> |
||||||
|
<b-row no-gutters style="height: 200px" > |
||||||
|
<b-col md="8" cols="7" class="border-right" style=";background-color: #EEEEEE"> |
||||||
|
<b-row align-h="center" v-if="user.image !== null"> |
||||||
|
<router-link |
||||||
|
:to="{name: 'User', params: {id: user.id } }" |
||||||
|
v-slot="{ href, route, navigate}" |
||||||
|
v-if="user.image !== '' && user.image !== null" |
||||||
|
> |
||||||
|
<div class="mb-2 imageUser"> |
||||||
|
<b-avatar :href="href" @click="navigate" |
||||||
|
:title="user.nom + ' ' + user.prenom" |
||||||
|
variant="light" |
||||||
|
v-bind:src="img" size="6rem" |
||||||
|
> |
||||||
|
</b-avatar> |
||||||
|
</div> |
||||||
|
</router-link> |
||||||
|
</b-row> |
||||||
|
<b-row align-h="center"> |
||||||
|
<b-col> |
||||||
|
<h4> {{user.prenom}} {{user.nom}}</h4> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row v-for="role in user.roles" v-bind:key="role.id"> |
||||||
|
<b-col class="d-flex justify-content-center align-items-center"> |
||||||
|
<h5> {{role.libelle}}</h5> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
</b-col> |
||||||
|
<b-col md="4" cols="5" class="d-flex flex-column justify-content-between"> |
||||||
|
|
||||||
|
<b-row > |
||||||
|
<b-col style="padding-bottom: 15px; padding-top: 15px"> |
||||||
|
<router-link |
||||||
|
:to="{name: 'User', params: {id: user.id } }" |
||||||
|
v-slot="{ href, route, navigate}" |
||||||
|
> |
||||||
|
|
||||||
|
<b-button :href="href" @click="navigate" class="actionButton" > |
||||||
|
<font-awesome-icon icon="edit" /> |
||||||
|
{{$t('edit')}}</b-button> |
||||||
|
</router-link> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row> |
||||||
|
<b-col style="padding-bottom: 15px; padding-top: 15px"> |
||||||
|
<b-button @click="deleteUser(user)" class="actionButton" > |
||||||
|
<font-awesome-icon icon="trash" /> |
||||||
|
{{$t('delete')}} |
||||||
|
</b-button> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
</b-card> |
||||||
|
|
||||||
|
<b-card v-if="user.isNew" no-body |
||||||
|
class="overflow-hidden rounded Desktopbutton cardUser"> |
||||||
|
<router-link |
||||||
|
:to="{name: 'User', params: {id: 0 } }" |
||||||
|
v-slot="{ href, route, navigate}"> |
||||||
|
<b-button :href="href" @click="navigate" |
||||||
|
style="width:100%; height: 100%" |
||||||
|
class="d-flex justify-content-center align-items-center"> |
||||||
|
<font-awesome-icon icon="plus-circle" style="width: 64px; height:64px" /> |
||||||
|
</b-button> |
||||||
|
</router-link> |
||||||
|
</b-card> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import axios from "axios"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "CardUser", |
||||||
|
props: |
||||||
|
{ |
||||||
|
method: {type: Function}, |
||||||
|
user: { |
||||||
|
type: Object |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() |
||||||
|
{ |
||||||
|
|
||||||
|
}, |
||||||
|
computed : |
||||||
|
{ |
||||||
|
img() |
||||||
|
{ |
||||||
|
return this.user != null && this.user.image!= null ? |
||||||
|
process.env.VUE_APP_IMAGE_STORE + this.user.image : |
||||||
|
''; |
||||||
|
}, |
||||||
|
}, |
||||||
|
methods: |
||||||
|
{ |
||||||
|
deleteUser : function(user) |
||||||
|
{ |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers:{'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
this.$bvModal.msgBoxConfirm('Vous allez supprimer l\'utilisateur \'' + user.login + '\'. Voulez-vous continuer ?', |
||||||
|
{ |
||||||
|
title: 'Suppression Utilisateur', |
||||||
|
size: 'sm', |
||||||
|
buttonSize: 'sm', |
||||||
|
okVariant: 'success', |
||||||
|
okTitle: 'Oui', |
||||||
|
cancelVariant: 'danger', |
||||||
|
cancelTitle: 'Non', |
||||||
|
footerClass: 'p-2', |
||||||
|
hideHeaderClose: false, |
||||||
|
centered: true |
||||||
|
}) |
||||||
|
.then(value => { |
||||||
|
if(value) { |
||||||
|
axios.delete(process.env.VUE_APP_BACK_USER+ "/api/users/" + user.id, |
||||||
|
axiosConfig |
||||||
|
).then(() => { |
||||||
|
this.$emit('send-message'); |
||||||
|
}); |
||||||
|
} |
||||||
|
}) |
||||||
|
.catch(err => { |
||||||
|
window.console.error(err.messages); |
||||||
|
}) |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.card-body |
||||||
|
{ |
||||||
|
padding-right: 0px; |
||||||
|
padding-left: 0px; |
||||||
|
padding-top: 0px; |
||||||
|
} |
||||||
|
.cardUser { |
||||||
|
max-width: 540px; |
||||||
|
height: 175px |
||||||
|
} |
||||||
|
.actionButton{ |
||||||
|
width:125px |
||||||
|
} |
||||||
|
|
||||||
|
.imageUser { |
||||||
|
width:96px; |
||||||
|
height:96px |
||||||
|
} |
||||||
|
|
||||||
|
.headerUser { |
||||||
|
position:fixed; /* fixing the position takes it out of html flow - knows |
||||||
|
nothing about where to locate itself except by browser |
||||||
|
coordinates */ |
||||||
|
left:0; /* top left corner should start at leftmost spot */ |
||||||
|
/*top:0; /* top left corner should start at topmost spot */ |
||||||
|
width:100vw; /* take up the full browser width */ |
||||||
|
z-index:50; /* high z index so other content scrolls underneath */ |
||||||
|
height: 100px; |
||||||
|
background-color: white; |
||||||
|
} |
||||||
|
@media (min-width: 50em) { |
||||||
|
.Desktopbutton { display: block; } |
||||||
|
.Mobilebutton { display: none; } |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 50em) { |
||||||
|
.Desktopbutton { display: none; } |
||||||
|
.Mobilebutton { display: block; } |
||||||
|
.MobileEntete {margin-top: 35px;} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,228 @@ |
|||||||
|
<template> |
||||||
|
<b-container fluid id="ListeUser" > |
||||||
|
<div class="headerUser" variant="dark"> |
||||||
|
<b-row align-h="center" |
||||||
|
style="padding-top: 15px;" |
||||||
|
> |
||||||
|
<h2>{{$t('user_title')}} </h2> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
<b-row align-v="center"> |
||||||
|
<b-col cols="8" lg="3" > |
||||||
|
<b-form-input v-model="search" |
||||||
|
@input="debounceInput" |
||||||
|
v-bind:placeholder="$t('search')" style="max-width: 400px"></b-form-input> |
||||||
|
</b-col> |
||||||
|
<b-col cols="1" lg="1"> |
||||||
|
<b-button @click="display"> |
||||||
|
<font-awesome-icon icon="search" /> |
||||||
|
</b-button> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
<b-row> |
||||||
|
<router-link |
||||||
|
:to="{name: 'User', params: {id: 0 } }" |
||||||
|
v-slot="{ href, route, navigate}"> |
||||||
|
<b-button :href="href" @click="navigate" class="Mobilebutton" |
||||||
|
style="width:100%"> |
||||||
|
<font-awesome-icon icon="plus-circle" /> |
||||||
|
</b-button> |
||||||
|
</router-link> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div style="padding-top: 135px" class="MobileEntete"> |
||||||
|
|
||||||
|
<b-row v-if="!isLoaded" class="text-center d-flex justify-content-center"> |
||||||
|
<b-spinner type="border" label="chargement en cours..."></b-spinner> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
<b-row v-if="isLoaded && users.length > 0" > |
||||||
|
<b-col cols="12" md="4" lg="4" v-for="user in users" v-bind:key="user.id" |
||||||
|
style="padding-right: 5px; padding-left: 5px; padding-top: 5px; padding-bottom: 5px"> |
||||||
|
<div> |
||||||
|
<card-user :user="user" @send-message="handleSendMessage"/> |
||||||
|
</div> |
||||||
|
</b-col> |
||||||
|
|
||||||
|
<infinite-loading @infinite="infiniteHandler" :identifier="infiniteId"> |
||||||
|
<div slot="no-more"></div> |
||||||
|
<div slot="no-results"></div> |
||||||
|
</infinite-loading> |
||||||
|
</b-row> |
||||||
|
<b-row class="mb-4" v-else> |
||||||
|
<p v-if="isLoaded">{{$t('user_list_empty')}} </p> |
||||||
|
</b-row> |
||||||
|
</div> |
||||||
|
</b-container> |
||||||
|
|
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import axios from 'axios'; |
||||||
|
import InfiniteLoading from 'vue-infinite-loading'; |
||||||
|
import CardUser from "./CardUser"; |
||||||
|
import { debounce } from "debounce"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "ListeUser", |
||||||
|
props:['value'], |
||||||
|
components: { |
||||||
|
CardUser, |
||||||
|
InfiniteLoading, |
||||||
|
}, |
||||||
|
|
||||||
|
data:function(){ |
||||||
|
return { |
||||||
|
page: 1, |
||||||
|
pageSize:4, |
||||||
|
search:'', |
||||||
|
infiniteId: + new Date(), |
||||||
|
users: [ |
||||||
|
], |
||||||
|
isLoaded: false |
||||||
|
} |
||||||
|
}, |
||||||
|
methods : |
||||||
|
{ |
||||||
|
handleSendMessage() { |
||||||
|
this.display(); |
||||||
|
}, |
||||||
|
debounceInput: debounce(function () { |
||||||
|
this.display(); |
||||||
|
}, 500,false), |
||||||
|
infiniteHandler : function($state) |
||||||
|
{ |
||||||
|
let that = this; |
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers:{'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/users?page=" + that.page |
||||||
|
+ "&pageSize=" + that.pageSize |
||||||
|
+ "&search=" + that.search, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
if (result.data.length) { |
||||||
|
this.page += 1; |
||||||
|
this.users.push(...result.data); |
||||||
|
$state.loaded(); |
||||||
|
} else { |
||||||
|
$state.complete(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
display : async function() |
||||||
|
{ |
||||||
|
this.isLoaded = false; |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers:{'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
this.page = 1; |
||||||
|
|
||||||
|
let result = await axios.get(process.env.VUE_APP_BACK_USER + "/api/users?page=" + this.page |
||||||
|
+ "&pageSize=" + this.pageSize |
||||||
|
+ "&search=" + this.search, |
||||||
|
axiosConfig |
||||||
|
); |
||||||
|
|
||||||
|
this.isLoaded = true; |
||||||
|
this.page += 1; |
||||||
|
this.users = [ {isNew:true}]; |
||||||
|
this.users.push(...result.data); |
||||||
|
this.infiniteId += 1; |
||||||
|
}, |
||||||
|
|
||||||
|
deleteUser : function(user) |
||||||
|
{ |
||||||
|
let that = this; |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers:{'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
this.$bvModal.msgBoxConfirm('Vous allez supprimer l\'utilisateur \'' + user.nom + '\'. Voulez-vous continuer ?', |
||||||
|
{ |
||||||
|
title: 'Suppression Utilisateur', |
||||||
|
size: 'sm', |
||||||
|
buttonSize: 'sm', |
||||||
|
okVariant: 'success', |
||||||
|
okTitle: 'Oui', |
||||||
|
cancelVariant: 'danger', |
||||||
|
cancelTitle: 'Non', |
||||||
|
footerClass: 'p-2', |
||||||
|
hideHeaderClose: false, |
||||||
|
centered: true |
||||||
|
}) |
||||||
|
.then(value => { |
||||||
|
if(value) { |
||||||
|
axios.delete(process.env.VUE_APP_BACK_USER+ "/api/users/" + user.id, |
||||||
|
axiosConfig |
||||||
|
).then(() => { |
||||||
|
that.display(); |
||||||
|
}); |
||||||
|
} |
||||||
|
}) |
||||||
|
.catch(err => { |
||||||
|
window.console.error(err.messages); |
||||||
|
}) |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
this.display(); |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.card-body |
||||||
|
{ |
||||||
|
padding-right: 0px; |
||||||
|
padding-left: 0px; |
||||||
|
padding-top: 0px; |
||||||
|
} |
||||||
|
.cardUser { |
||||||
|
max-width: 540px; |
||||||
|
height: 175px |
||||||
|
} |
||||||
|
.actionButton{ |
||||||
|
width:125px |
||||||
|
} |
||||||
|
|
||||||
|
.imageUser { |
||||||
|
width:96px; |
||||||
|
height:96px |
||||||
|
} |
||||||
|
|
||||||
|
.headerUser { |
||||||
|
position:fixed; /* fixing the position takes it out of html flow - knows |
||||||
|
nothing about where to locate itself except by browser |
||||||
|
coordinates */ |
||||||
|
left:0; /* top left corner should start at leftmost spot */ |
||||||
|
/*top:0; /* top left corner should start at topmost spot */ |
||||||
|
width:100vw; /* take up the full browser width */ |
||||||
|
z-index:50; /* high z index so other content scrolls underneath */ |
||||||
|
height: 100px; |
||||||
|
background-color: white; |
||||||
|
} |
||||||
|
@media (min-width: 50em) { |
||||||
|
.Desktopbutton { display: block; } |
||||||
|
.Mobilebutton { display: none; } |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 50em) { |
||||||
|
.Desktopbutton { display: none; } |
||||||
|
.Mobilebutton { display: block; } |
||||||
|
.MobileEntete {margin-top: 35px;} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,328 @@ |
|||||||
|
<template> |
||||||
|
|
||||||
|
<b-container fluid id="User" > |
||||||
|
<!-- <b-row class="d-flex justify-content-center" style="padding-top: 15px;"> |
||||||
|
<h2>{{titre}} </h2> |
||||||
|
</b-row>--> |
||||||
|
|
||||||
|
<b-row v-if="!isLoaded" class="text-center d-flex justify-content-center"> |
||||||
|
<b-spinner type="border" label="chargement en cours..."></b-spinner> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
<b-row v-if="isLoaded && user !== null"> |
||||||
|
<b-col cols="12" md="2"> |
||||||
|
<b-row align-v="center"> |
||||||
|
<b-col > |
||||||
|
<b-avatar |
||||||
|
variant="light" |
||||||
|
v-bind:src="img" size="10rem" |
||||||
|
> |
||||||
|
</b-avatar> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row align-v="center"> |
||||||
|
<b-col class="d-flex justify-content-center align-items-center"> |
||||||
|
<b-button v-if="hasImage" variant="danger" class="Desktop" @click="clearImage">{{$t('clear')}}</b-button> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
</b-col> |
||||||
|
<b-col cols="12" md="10"> |
||||||
|
<b-row align-v="center" style="padding-top: 5px"> |
||||||
|
<b-col md="1" cols="4"> |
||||||
|
<label>{{$t('user_detail_prenom')}}</label> |
||||||
|
</b-col> |
||||||
|
<b-col md="5" cols="8"> |
||||||
|
<b-form-input v-model="user.prenom" |
||||||
|
:state="validFirstName" |
||||||
|
v-bind:placeholder="$t('user_detail_prenom')"></b-form-input> |
||||||
|
</b-col> |
||||||
|
<b-col md="1" cols="4"> |
||||||
|
<label>{{$t('user_detail_nom')}}</label> |
||||||
|
</b-col> |
||||||
|
<b-col md="5" cols="8"> |
||||||
|
<b-form-input number v-model="user.nom" |
||||||
|
:state="validName" |
||||||
|
v-bind:placeholder="$t('user_detail_nom')"></b-form-input> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row> |
||||||
|
<b-col md="1" cols="4"> |
||||||
|
<label>{{$t('user_detail_username')}}</label> |
||||||
|
</b-col> |
||||||
|
<b-col md="5" cols="8"> |
||||||
|
<b-form-input v-model="user.userName" |
||||||
|
:state="validUserName" |
||||||
|
v-bind:placeholder="$t('user_detail_username')"></b-form-input> |
||||||
|
</b-col> |
||||||
|
<!-- <b-col md="1" cols="4"> |
||||||
|
<label>{{$t('user_detail_mail')}}</label> |
||||||
|
</b-col> |
||||||
|
<b-col md="5" cols="8"> |
||||||
|
<b-form-input mail v-model="user.email" v-bind:placeholder="$t('user_detail_mail')"></b-form-input> |
||||||
|
</b-col>--> |
||||||
|
</b-row> |
||||||
|
<b-row align-v="center" style="padding-top: 5px"> |
||||||
|
<b-col md="1" cols="4"> |
||||||
|
<label> {{$t('user_detail_id_keycloak')}}</label> |
||||||
|
</b-col> |
||||||
|
<b-col md="5" cols="8"> |
||||||
|
<b-form-input :disabled="!isAdmin" v-model="user.keycloakId" v-bind:placeholder="$t('user_detail_id_keycloak')"></b-form-input> |
||||||
|
</b-col> |
||||||
|
<b-col md="1" class="Desktop"> |
||||||
|
<label>{{$t('user_detail_image')}}</label> |
||||||
|
</b-col> |
||||||
|
<b-col md="5" class="Desktop"> |
||||||
|
<b-form-file |
||||||
|
v-model="file" |
||||||
|
plain |
||||||
|
></b-form-file> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row class="d-flex justify-content-center align-items-center" style="padding-top: 10px"> |
||||||
|
<h5>{{$t('user_detail_roles')}}</h5> |
||||||
|
</b-row> |
||||||
|
<b-row> |
||||||
|
<b-col> |
||||||
|
<b-row v-for="role in roles" v-bind:key="role.id"> |
||||||
|
<b-col> |
||||||
|
{{role.name}} |
||||||
|
</b-col> |
||||||
|
<b-col> |
||||||
|
<toggle-button |
||||||
|
v-model="role.isActived" |
||||||
|
:value="false" |
||||||
|
:font-size="16" |
||||||
|
:sync="true" |
||||||
|
/> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
|
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
<b-row v-if="isLoaded" > |
||||||
|
<b-col class="d-flex justify-content-between align-items-center"> |
||||||
|
<router-link |
||||||
|
:to="{name: 'ListeUser' }" |
||||||
|
v-slot="{ href, route, navigate}" |
||||||
|
> |
||||||
|
<b-button :href="href" @click="navigate" variant="danger"><font-awesome-icon icon="arrow-alt-circle-left" /> {{$t('back')}}</b-button> |
||||||
|
</router-link> |
||||||
|
|
||||||
|
<photo v-bind:snapshot.sync="photo"></photo> |
||||||
|
|
||||||
|
<b-button @click="saveAndQuit" variant="success" v-bind:disabled="!isValid"><font-awesome-icon icon="check" /> {{$t('valid')}}</b-button> |
||||||
|
</b-col> |
||||||
|
</b-row> |
||||||
|
</b-container> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import axios from 'axios'; |
||||||
|
import photo from '../Capture/CapturePhoto'; |
||||||
|
import Vue from "vue"; |
||||||
|
|
||||||
|
|
||||||
|
Vue.use(photo); |
||||||
|
|
||||||
|
const base64Encode = data => |
||||||
|
new Promise((resolve, reject) => { |
||||||
|
const reader = new FileReader(); |
||||||
|
reader.readAsDataURL(data); |
||||||
|
reader.onload = () => resolve(reader.result); |
||||||
|
reader.onerror = error => reject(error); |
||||||
|
}); |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "User", |
||||||
|
components: { photo}, |
||||||
|
data:function(){ |
||||||
|
return { |
||||||
|
user: null, |
||||||
|
id: null, |
||||||
|
componentKey:0, |
||||||
|
file : null, |
||||||
|
photo: null, |
||||||
|
img:null, |
||||||
|
roles: null, |
||||||
|
isLoaded : false |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
isAdmin() { |
||||||
|
|
||||||
|
return this.$store.state.roles.find(l => l.name == 'Administrators' && l.isActived) != null; |
||||||
|
}, |
||||||
|
isValid() |
||||||
|
{ |
||||||
|
return this.validFirstName && this.validName && this.validUserName; |
||||||
|
}, |
||||||
|
validFirstName(){ |
||||||
|
return this.user != null && this.user.prenom !== '' && this.user.prenom !== null; |
||||||
|
}, |
||||||
|
validName(){ |
||||||
|
|
||||||
|
return this.user != null && this.user.nom !== '' && this.user.nom !== null; |
||||||
|
}, |
||||||
|
validUserName(){ |
||||||
|
|
||||||
|
return this.user != null && this.user.userName !== '' && this.user.userName !== null && this.user.userName !== undefined; |
||||||
|
}, |
||||||
|
hasImage() { |
||||||
|
return !!this.img; |
||||||
|
}, |
||||||
|
titre(){ |
||||||
|
return (this.user != null) ? this.user.prenom + ' ' + this.user.nom : this.$i18n.t('user_detail_title'); |
||||||
|
} |
||||||
|
}, |
||||||
|
watch : |
||||||
|
{ |
||||||
|
file : function (newValue, oldValue) { |
||||||
|
if (newValue !== oldValue) { |
||||||
|
if (newValue) { |
||||||
|
base64Encode(newValue) |
||||||
|
.then(value => { |
||||||
|
this.user.image= newValue.name; |
||||||
|
this.img = value; |
||||||
|
}) |
||||||
|
.catch(() => { |
||||||
|
this.img = null; |
||||||
|
}); |
||||||
|
} else { |
||||||
|
this.img = null; |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
photo: function(val) |
||||||
|
{ |
||||||
|
this.img =val.photo; |
||||||
|
}, |
||||||
|
|
||||||
|
}, |
||||||
|
methods : |
||||||
|
{ |
||||||
|
clearImage() { |
||||||
|
this.img = null; |
||||||
|
this.file = null; |
||||||
|
this.photo = null; |
||||||
|
|
||||||
|
this.user.image="user_unknown.png"; |
||||||
|
}, |
||||||
|
saveAndQuit : async function() |
||||||
|
{ |
||||||
|
await this.save(); |
||||||
|
await this.$router.push({name: 'ListeUser'}); |
||||||
|
}, |
||||||
|
save : async function() |
||||||
|
{ |
||||||
|
let that = this; |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers:{'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
let result = await axios.put(process.env.VUE_APP_BACK_USER + "/api/users/" + this.id, this.user, axiosConfig); |
||||||
|
that.user = result.data; |
||||||
|
let data = new FormData(); |
||||||
|
|
||||||
|
if(that.file !== null) { |
||||||
|
data.set('file', that.file) |
||||||
|
await axios.post(process.env.VUE_APP_BACK_USER + '/api/users/' + that.id + '/loadimage', data, { |
||||||
|
headers: { |
||||||
|
'Content-Type': 'multipart/form-data', |
||||||
|
'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
else if(that.photo != null) |
||||||
|
{ |
||||||
|
data.set('base64', that.img.split(",")[1]); |
||||||
|
await axios.post(process.env.VUE_APP_BACK_USER + "/api/users/" + that.id + '/loadimage64',data, |
||||||
|
{ |
||||||
|
headers: { |
||||||
|
'Content-Type': 'multipart/form-data', |
||||||
|
'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
} |
||||||
|
); |
||||||
|
that.componentKey += 1; |
||||||
|
} |
||||||
|
|
||||||
|
await axios.put(process.env.VUE_APP_BACK_USER + "/api/roles/" + this.user.keycloakId, this.roles, axiosConfig); |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
this.id = this.$route.params.id; |
||||||
|
|
||||||
|
let axiosConfig = { |
||||||
|
withCredentials: true, |
||||||
|
headers: { |
||||||
|
'Authorization': 'Bearer ' + sessionStorage.getItem("react-token"), |
||||||
|
} |
||||||
|
}; |
||||||
|
this.isLoaded = false; |
||||||
|
if(this.id !== 0) { |
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/users/" + this.id, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
this.user = result.data; |
||||||
|
this.img = process.env.VUE_APP_IMAGE_STORE + this.user.image; |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/roles/bykeycloakid?keycloakid=" + this.user.keycloakId, |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
this.isLoaded = true; |
||||||
|
this.roles = result.data; |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
this.user = {id:0, nom:'', prenom:'', keycloakId:''} |
||||||
|
|
||||||
|
axios.get(process.env.VUE_APP_BACK_USER + "/api/roles", |
||||||
|
axiosConfig |
||||||
|
).then(result => { |
||||||
|
this.isLoaded = true; |
||||||
|
this.roles = result.data; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.row |
||||||
|
{ |
||||||
|
padding-top: 5px |
||||||
|
} |
||||||
|
.card-body |
||||||
|
{ |
||||||
|
padding-right: 0px; |
||||||
|
padding-left: 0px; |
||||||
|
padding-top: 0px; |
||||||
|
padding-bottom: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
.collapsed > .when-opened, |
||||||
|
:not(.collapsed) > .when-closed { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
@media (min-width: 50em) { |
||||||
|
.Desktop { display: block; } |
||||||
|
.Mobile { display: none; } |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 50em) { |
||||||
|
.Desktop { display: none; } |
||||||
|
.Mobile { display: block; } |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,41 @@ |
|||||||
|
export default { |
||||||
|
en: { |
||||||
|
}, |
||||||
|
'fr-FR': { |
||||||
|
edit:"Editer", |
||||||
|
delete:"Supprimer", |
||||||
|
add:"ajouter", |
||||||
|
logout:"Logout", |
||||||
|
login:"Login", |
||||||
|
save:"Sauvegarder", |
||||||
|
back:"Retour", |
||||||
|
valid:"Valider", |
||||||
|
unload:"Décharge", |
||||||
|
search:"Recherche", |
||||||
|
clear:"Effacer", |
||||||
|
inprogress:"chargement en cours...", |
||||||
|
|
||||||
|
title:"Coviiiid", |
||||||
|
sub_title:"Coviiiid !!!!! Je sais où tu t'caches ! Viens là que j'te bute !", |
||||||
|
object_temperature : "T° objet", |
||||||
|
ambient_temperature : "T° ambiante", |
||||||
|
emissivity:"Emissivité", |
||||||
|
temperature_unit : "°C", |
||||||
|
temperature_acq:"Coviiiid", |
||||||
|
temperature_chart_label:"Température", |
||||||
|
|
||||||
|
user_title : "Utilisateurs", |
||||||
|
user_list_empty: "Aucun utilisateur", |
||||||
|
|
||||||
|
user_detail_title: "Utilisateur", |
||||||
|
user_detail_nom: "Nom", |
||||||
|
user_detail_prenom: "Prénom", |
||||||
|
user_detail_image:"Image", |
||||||
|
user_detail_id_keycloak:'idKeycloak', |
||||||
|
user_detail_username : 'login', |
||||||
|
user_detail_mail : 'Email', |
||||||
|
user_detail_roles : 'Rôles', |
||||||
|
user_detail_photo:"photo", |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
import Vue from 'vue' |
||||||
|
import App from './App.vue' |
||||||
|
import router from './router/index' |
||||||
|
import VueI18n from 'vue-i18n' |
||||||
|
import messages from './lang/messages' |
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core' |
||||||
|
import { fas } from '@fortawesome/free-solid-svg-icons' |
||||||
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' |
||||||
|
import axios from "axios"; |
||||||
|
import UserService from "./services/UserService"; |
||||||
|
|
||||||
|
|
||||||
|
import ToggleButton from 'vue-js-toggle-button' |
||||||
|
import Vuex from 'vuex' |
||||||
|
|
||||||
|
|
||||||
|
library.add(fas) |
||||||
|
Vue.component('font-awesome-icon', FontAwesomeIcon) |
||||||
|
Vue.config.productionTip = false; |
||||||
|
Vue.use(VueI18n) |
||||||
|
Vue.use(ToggleButton); |
||||||
|
|
||||||
|
let locale = navigator.language; |
||||||
|
const i18n = new VueI18n({ |
||||||
|
fallbackLocale: 'fr', |
||||||
|
locale: locale, |
||||||
|
messages |
||||||
|
}) |
||||||
|
|
||||||
|
Vue.use(Vuex) |
||||||
|
const store = new Vuex.Store({ |
||||||
|
state : { |
||||||
|
roles: [], |
||||||
|
user:null, |
||||||
|
}, |
||||||
|
mutations: { |
||||||
|
updateRole (state, roles) { |
||||||
|
this.state.roles = roles; |
||||||
|
}, |
||||||
|
updateUser (state, user) { |
||||||
|
this.state.user = user; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
new Vue({ |
||||||
|
router, |
||||||
|
i18n, |
||||||
|
store, |
||||||
|
mounted(){ |
||||||
|
axios.interceptors.response.use( (response) => { |
||||||
|
// Return a successful response back to the calling service
|
||||||
|
return response; |
||||||
|
}, (error) => { |
||||||
|
// Return any error which is not due to authentication back to the calling service
|
||||||
|
if (error.response.status !== 401) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
reject(error); |
||||||
|
}); |
||||||
|
} |
||||||
|
else if (error.response.status === 401) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
UserService.doLogout(); |
||||||
|
UserService.initKeycloak( function () { |
||||||
|
|
||||||
|
sessionStorage.setItem("react-token", UserService.getToken()); |
||||||
|
sessionStorage.setItem("react-refresh-token", UserService.getRefreshToken()); |
||||||
|
}, UserService.doLogin); |
||||||
|
reject(error); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
}) |
||||||
|
}, |
||||||
|
render: h => h(App), |
||||||
|
}).$mount('#app') |
@ -0,0 +1,33 @@ |
|||||||
|
import Vue from 'vue' |
||||||
|
import Router from 'vue-router' |
||||||
|
import Temperature from "../components/Temperature"; |
||||||
|
import User from "../components/User/User"; |
||||||
|
import ListeUser from "../components/User/ListeUser"; |
||||||
|
|
||||||
|
Vue.use(Router) |
||||||
|
|
||||||
|
export default new Router({ |
||||||
|
routes: [ |
||||||
|
{ |
||||||
|
path: '*', |
||||||
|
name: 'Temperature', |
||||||
|
component: Temperature |
||||||
|
}, |
||||||
|
{ |
||||||
|
path: '/', |
||||||
|
name: 'Temperature', |
||||||
|
component: Temperature |
||||||
|
}, |
||||||
|
|
||||||
|
{ |
||||||
|
path: '/user/:id', |
||||||
|
name: 'User', |
||||||
|
component: User |
||||||
|
}, |
||||||
|
{ |
||||||
|
path: '/users', |
||||||
|
name: 'ListeUser', |
||||||
|
component: ListeUser |
||||||
|
}, |
||||||
|
] |
||||||
|
}) |
@ -0,0 +1,52 @@ |
|||||||
|
import {HubConnectionBuilder, LogLevel} from "@aspnet/signalr"; |
||||||
|
import Vue from "vue"; |
||||||
|
|
||||||
|
export default class TemperatureHub |
||||||
|
{ |
||||||
|
hubConnection; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.hubConnection = new HubConnectionBuilder() |
||||||
|
.withUrl(process.env.VUE_APP_TEMPERATURE_HUB) |
||||||
|
.configureLogging(LogLevel.Information) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
|
||||||
|
start() { |
||||||
|
let startedPromise = null; |
||||||
|
let that = this; |
||||||
|
startedPromise = this.hubConnection.start().catch(() => { |
||||||
|
return new Promise((resolve, reject) => |
||||||
|
setTimeout(() => that.start().then(resolve).catch(reject), process.env.VUE_APP_TIMEOUT_TEMPERATURE_HUB)) |
||||||
|
}) |
||||||
|
return startedPromise; |
||||||
|
} |
||||||
|
|
||||||
|
startHub() |
||||||
|
{ |
||||||
|
let that = this; |
||||||
|
this.hubConnection.onclose(() => |
||||||
|
{ |
||||||
|
that.start(); |
||||||
|
}/**/); |
||||||
|
|
||||||
|
this.start(); |
||||||
|
|
||||||
|
// use new Vue instance as an event bus
|
||||||
|
let questionHub = new Vue() |
||||||
|
// every component will use this.$questionHub to access the event bus
|
||||||
|
|
||||||
|
// Forward server side SignalR events through $questionHub, where components will listen to them
|
||||||
|
this.hubConnection.on(process.env.VUE_APP_MSG_TEMPERATURE_HUB, (msg) => { |
||||||
|
questionHub.$emit(process.env.VUE_APP_EMIT_MSG_TEMPERATURE, JSON.parse(msg) ) |
||||||
|
}) |
||||||
|
|
||||||
|
return questionHub; |
||||||
|
} |
||||||
|
|
||||||
|
async stopHub() |
||||||
|
{ |
||||||
|
await this.hubConnection.stop(); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
import Keycloak from "keycloak-js"; |
||||||
|
|
||||||
|
const _kc = new Keycloak('/keycloak.json'); |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes Keycloak instance and calls the provided callback function if successfully authenticated. |
||||||
|
* |
||||||
|
* @param onAuthenticatedCallback |
||||||
|
*/ |
||||||
|
const initKeycloak = (onAuthenticatedCallback, onUnAuthentificatedCallback) => { |
||||||
|
_kc.init({ |
||||||
|
onLoad: 'check-sso', |
||||||
|
flow: 'implicit', |
||||||
|
promiseType: 'native', |
||||||
|
}) |
||||||
|
.then((authenticated) => { |
||||||
|
if (authenticated) { |
||||||
|
onAuthenticatedCallback(); |
||||||
|
} else { |
||||||
|
window.console.warn("not authenticated!"); |
||||||
|
onUnAuthentificatedCallback(); |
||||||
|
} |
||||||
|
}) |
||||||
|
}; |
||||||
|
|
||||||
|
const doLogin = _kc.login; |
||||||
|
|
||||||
|
const doLogout = _kc.logout; |
||||||
|
|
||||||
|
const getToken = () => _kc.token; |
||||||
|
|
||||||
|
const getRefreshToken = () => _kc.refreshToken; |
||||||
|
|
||||||
|
const updateToken = (successCallback) => { |
||||||
|
return _kc.updateToken(5) |
||||||
|
.then(successCallback) |
||||||
|
.catch(doLogin) |
||||||
|
}; |
||||||
|
|
||||||
|
const getUsername = () => _kc.tokenParsed; |
||||||
|
|
||||||
|
export default { |
||||||
|
initKeycloak, |
||||||
|
doLogin, |
||||||
|
doLogout, |
||||||
|
getToken, |
||||||
|
getRefreshToken, |
||||||
|
updateToken, |
||||||
|
getUsername, |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
import axios from 'axios'; |
||||||
|
import { Promise } from "es6-promise"; |
||||||
|
|
||||||
|
|
||||||
|
export default () => { |
||||||
|
|
||||||
|
axios.interceptors.response.use( (response) => { |
||||||
|
// Return a successful response back to the calling service
|
||||||
|
return response; |
||||||
|
}, (error) => { |
||||||
|
// Return any error which is not due to authentication back to the calling service
|
||||||
|
if (error.response.status !== 401) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
reject(error); |
||||||
|
}); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
module.exports = { |
||||||
|
devServer: { |
||||||
|
https: false, |
||||||
|
port:8080 |
||||||
|
} |
||||||
|
} |