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 |
||||
} |
||||
} |