moved to bootstrap-vue, there is a bug in vee-validate and bootstrap-vue >rc15
This commit is contained in:
3102
package-lock.json
generated
3102
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -9,14 +9,20 @@
|
|||||||
"test:unit": "vue-cli-service test:unit"
|
"test:unit": "vue-cli-service test:unit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^1.2.17",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^5.8.1",
|
||||||
|
"@fortawesome/vue-fontawesome": "^0.1.6",
|
||||||
"@vue/cli": "^3.6.3",
|
"@vue/cli": "^3.6.3",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"bootstrap": "^4.3.1",
|
"bootstrap": "^4.3.1",
|
||||||
"bootstrap-vue": "^2.0.0-rc.13",
|
"bootstrap-vue": "^2.0.0-rc.15",
|
||||||
|
"i": "^0.3.6",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"js-cookie": "^2.2.0",
|
"js-cookie": "^2.2.0",
|
||||||
|
"npm": "^6.9.0",
|
||||||
"popper.js": "^1.15.0",
|
"popper.js": "^1.15.0",
|
||||||
"vue": "^2.6.6",
|
"vee-validate": "^2.2.4",
|
||||||
|
"vue": "^2.6.10",
|
||||||
"vue-axios": "^2.1.4",
|
"vue-axios": "^2.1.4",
|
||||||
"vue-class-component": "^6.0.0",
|
"vue-class-component": "^6.0.0",
|
||||||
"vue-cookies": "^1.5.13",
|
"vue-cookies": "^1.5.13",
|
||||||
@@ -24,6 +30,7 @@
|
|||||||
"vue-i18n": "^8.9.0",
|
"vue-i18n": "^8.9.0",
|
||||||
"vue-property-decorator": "^7.0.0",
|
"vue-property-decorator": "^7.0.0",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
|
"vue-spinner": "^1.0.3",
|
||||||
"vue-sweetalert2": "^1.6.4",
|
"vue-sweetalert2": "^1.6.4",
|
||||||
"vuejs-logger": "^1.5.3",
|
"vuejs-logger": "^1.5.3",
|
||||||
"vuex": "^3.0.1",
|
"vuex": "^3.0.1",
|
||||||
@@ -43,6 +50,6 @@
|
|||||||
"node-sass": "^4.11.0",
|
"node-sass": "^4.11.0",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"typescript": "^3.0.0",
|
"typescript": "^3.0.0",
|
||||||
"vue-template-compiler": "^2.5.21"
|
"vue-template-compiler": "^2.6.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<!--<link href="//netdna.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" />-->
|
|
||||||
<style>
|
<style>
|
||||||
a { cursor: pointer; }
|
a { cursor: pointer; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
171
src/App.vue
171
src/App.vue
@@ -1,77 +1,124 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div id="nav">
|
<sync-loader :loading="isLoading"></sync-loader>
|
||||||
<router-link to="/">Home</router-link> |
|
<div id="nav">
|
||||||
<router-link to="/about">About</router-link> |
|
<router-link to="/">Home</router-link>
|
||||||
<router-link to="/login">Login</router-link> |
|
|
|
||||||
<router-link to="/profile">Profile</router-link> |
|
<router-link to="/about">About</router-link>
|
||||||
<!-- Example split danger button -->
|
|
|
||||||
<div class="btn-group">
|
<router-link to="/login">Login</router-link>
|
||||||
<button type="button" onclick="location.href='/admin'" class="btn btn-danger">Admin</button>
|
|
|
||||||
<button type="button" class="btn btn-danger dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<router-link to="/profile">Profile</router-link>
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
|
|
||||||
</button>
|
<router-link :to="{ name: 'rooms' }">{{ $t('rooms') }}</router-link>
|
||||||
<div class="dropdown-menu">
|
|
|
||||||
<router-link class="dropdown-item" :to="{ name: 'admin.user' }">{{ $t('user') }}</router-link>
|
<router-link :to="{ name: 'recorders' }">{{ $t('recorders') }}</router-link>
|
||||||
<router-link class="dropdown-item" :to="{ name: 'admin.group' }">{{ $t('group') }}</router-link>
|
|
|
||||||
<a class="dropdown-item" href="#">Something else here</a>
|
<router-link :to="{ name: 'commands' }">{{ $t('commands') }}</router-link>
|
||||||
<div class="dropdown-divider"></div>
|
|
|
||||||
<a class="dropdown-item" href="#">Separated link</a>
|
<!-- Example split danger button -->
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" onclick="location.href='/admin'" class="btn btn-danger">Admin</button>
|
||||||
|
<button type="button" class="btn btn-danger dropdown-toggle dropdown-toggle-split"
|
||||||
|
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<router-link class="dropdown-item" :to="{ name: 'admin.user' }">{{ $t('user') }}</router-link>
|
||||||
|
<router-link class="dropdown-item" :to="{ name: 'admin.group' }">{{ $t('group') }}</router-link>
|
||||||
|
<a class="dropdown-item" href="#">Something else here</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="#">Separated link</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> |
|
|
|
||||||
<span>({{tokenValidity}})</span> |
|
<span>({{tokenValidity}})</span> |
|
||||||
<span>({{refreshTokenValidity}})</span>
|
<span>({{refreshTokenValidity}})</span>
|
||||||
|
</div>
|
||||||
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
<router-view/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getRemainingJwtValiditySeconds} from "@/utils";
|
import {getRemainingJwtValiditySeconds} from '@/utils';
|
||||||
|
import SyncLoader from 'vue-spinner/src/SyncLoader.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
components: {
|
||||||
return {
|
SyncLoader,
|
||||||
tokenValidity: -1,
|
},
|
||||||
refreshTokenValidity: -1,
|
data() {
|
||||||
};
|
return {
|
||||||
},
|
isLoading: false,
|
||||||
methods: {
|
tokenValidity: -1,
|
||||||
},
|
refreshTokenValidity: -1,
|
||||||
mounted () {
|
};
|
||||||
|
},
|
||||||
this.$nextTick(() =>{
|
methods: {
|
||||||
window.setInterval(() => {
|
// todo ...
|
||||||
// this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
|
},
|
||||||
this.tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
|
mounted() {
|
||||||
this.refreshTokenValidity = getRemainingJwtValiditySeconds(this.$store.state.refresh_token);
|
this.$nextTick(() => {
|
||||||
// this.$log.debug(this.$store.state);
|
window.setInterval(() => {
|
||||||
}, 1000);
|
// this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
|
||||||
});
|
this.tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
|
||||||
|
this.refreshTokenValidity = getRemainingJwtValiditySeconds(this.$store.state.refresh_token);
|
||||||
|
// this.$log.debug(this.$store.state);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../node_modules/bootstrap/scss/bootstrap.scss';
|
@import '../node_modules/bootstrap/scss/bootstrap.scss';
|
||||||
#app {
|
|
||||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
#app {
|
||||||
-webkit-font-smoothing: antialiased;
|
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-webkit-font-smoothing: antialiased;
|
||||||
text-align: center;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
}
|
}
|
||||||
#nav {
|
|
||||||
padding: 30px;
|
#nav {
|
||||||
a {
|
padding: 30px;
|
||||||
font-weight: bold;
|
text-align: center;
|
||||||
color: #2c3e50;
|
a {
|
||||||
&.router-link-exact-active {
|
font-weight: bold;
|
||||||
color: #42b983;
|
color: #2c3e50;
|
||||||
|
|
||||||
|
&.router-link-exact-active {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Absolute Center Spinner */
|
||||||
|
.v-spinner {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
// height: 2em;
|
||||||
|
// width: 2em;
|
||||||
|
overflow: visible;
|
||||||
|
margin: auto;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transparent Overlay */
|
||||||
|
.v-spinner:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// RepositoryFactory.js
|
|
||||||
|
|
||||||
import GroupRepository from "./groupRepository";
|
|
||||||
import UserRepository from "./userRepository";
|
|
||||||
|
|
||||||
const repositories = {
|
|
||||||
group: GroupRepository,
|
|
||||||
user: UserRepository,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RepositoryFactory = {
|
|
||||||
get: name => repositories[name],
|
|
||||||
};
|
|
||||||
28
src/api/RepositoryFactory.ts
Normal file
28
src/api/RepositoryFactory.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// RepositoryFactory.js
|
||||||
|
|
||||||
|
import GroupRepository from './groupRepository';
|
||||||
|
import UserRepository from './userRepository';
|
||||||
|
import RoomRepository from './roomRepository';
|
||||||
|
import RecorderRepository from './recorderRepository';
|
||||||
|
|
||||||
|
|
||||||
|
export default function get(name: string) {
|
||||||
|
switch (name) {
|
||||||
|
case 'group': {
|
||||||
|
return GroupRepository;
|
||||||
|
}
|
||||||
|
case 'recorder': {
|
||||||
|
return RecorderRepository;
|
||||||
|
}
|
||||||
|
case 'room': {
|
||||||
|
return RoomRepository;
|
||||||
|
}
|
||||||
|
case 'user': {
|
||||||
|
return UserRepository;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// statements;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
// groupRepository.js
|
// groupRepository.js
|
||||||
|
|
||||||
import Repository from "./Repository";
|
// @ts-ignore
|
||||||
|
import Repository from './Repository';
|
||||||
|
|
||||||
const resource = "/group";
|
const resource = '/group';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getGroups() {
|
getGroups() {
|
||||||
return Repository.get(`${resource}`);
|
return Repository.get(`${resource}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
getGroup(groupId) {
|
getGroup(groupId: number) {
|
||||||
return Repository.get(`${resource}/${groupId}`);
|
return Repository.get(`${resource}/${groupId}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
createGroup(groupData) {
|
createGroup(groupData: any) {
|
||||||
return Repository.post(`${resource}`, groupData);
|
return Repository.post(`${resource}`, groupData);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -58,6 +58,6 @@ export function fetchUserGroup(jwt: any, groupId: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchProfile(jwt: any) {
|
export function fetchProfile(jwt: any) {
|
||||||
Vue.$log.debug("JWT: "+ jwt);
|
Vue.$log.debug('JWT: ' + jwt);
|
||||||
return axios.get(`${API_URL}/v1/user/profile`, { headers: { Authorization: `Bearer ${jwt}` } });
|
return axios.get(`${API_URL}/v1/user/profile`, { headers: { Authorization: `Bearer ${jwt}` } });
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/api/recorderRepository.ts
Normal file
24
src/api/recorderRepository.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// groupRepository.ts
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import Repository from './Repository';
|
||||||
|
|
||||||
|
const resource = '/recorder';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getRecorders() {
|
||||||
|
return Repository.get(`${resource}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
getRecorder(recorderId: number) {
|
||||||
|
return Repository.get(`${resource}/${recorderId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
createRecorder(recorderData: any) {
|
||||||
|
return Repository.post(`${resource}`, recorderData);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRecorder(recorderId: number, recorderData: any) {
|
||||||
|
return Repository.put(`${resource}/${recorderId}`, recorderData);
|
||||||
|
},
|
||||||
|
};
|
||||||
24
src/api/roomRepository.ts
Normal file
24
src/api/roomRepository.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// groupRepository.ts
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import Repository from './Repository';
|
||||||
|
|
||||||
|
const resource = '/room';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getRooms() {
|
||||||
|
return Repository.get(`${resource}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
getRoom(roomId: number) {
|
||||||
|
return Repository.get(`${resource}/${roomId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
createRoom(roomData: any) {
|
||||||
|
return Repository.post(`${resource}`, roomData);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRoom(roomId: number, roomData: any) {
|
||||||
|
return Repository.put(`${resource}/${roomId}`, roomData);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
// groupRepository.js
|
// groupRepository.ts
|
||||||
|
|
||||||
import Repository from "./Repository";
|
// @ts-ignore
|
||||||
|
import Repository from './Repository';
|
||||||
|
|
||||||
const resource = "/user";
|
const resource = '/user';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getUsers() {
|
getUsers() {
|
||||||
return Repository.get(`${resource}`);
|
return Repository.get(`${resource}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
getUser(userId) {
|
getUser(userId: number) {
|
||||||
return Repository.get(`${resource}/${userId}`);
|
return Repository.get(`${resource}/${userId}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
createUser(userData) {
|
createUser(userData: any) {
|
||||||
return Repository.post(`${resource}`, userData);
|
return Repository.post(`${resource}`, userData);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -22,29 +22,31 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { EventBus } from '@/utils'
|
import {EventBus} from '@/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
email: '',
|
email: '',
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
authenticate () {
|
authenticate() {
|
||||||
this.$store.dispatch('login', { email: this.email, password: this.password })
|
this.$store.dispatch('login', {email: this.email, password: this.password})
|
||||||
.then(() => this.$router.push('/'));
|
.then(() => this.$router.push('/'));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
this.$log.debug("Admin: mounting...");
|
this.$log.debug('Admin: mounting...');
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
|
// todo ...
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// todo ...
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="col in group_columns" v-on:click="sortTable(col)">{{col}}
|
<th v-for="col in group_columns" v-on:click="sortTable(col)">{{col}}
|
||||||
<div class="arrow" v-if="col == sortColumn" v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
|
<div class="arrow" v-if="col == sortColumn"
|
||||||
|
v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -26,7 +27,8 @@
|
|||||||
<div class="number"
|
<div class="number"
|
||||||
v-for="i in num_group_pages()"
|
v-for="i in num_group_pages()"
|
||||||
v-bind:class="[i == currentPage ? 'active' : '']"
|
v-bind:class="[i == currentPage ? 'active' : '']"
|
||||||
v-on:click="change_page(i)">{{i}}</div>
|
v-on:click="change_page(i)">{{i}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -50,17 +52,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { EventBus } from '@/utils'
|
import {EventBus} from '@/utils';
|
||||||
import { RepositoryFactory} from "@/api/RepositoryFactory";
|
import getRepository from '@/api/RepositoryFactory';
|
||||||
const GroupRepository = RepositoryFactory.get('group');
|
|
||||||
|
const GroupRepository = getRepository('group');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
groups: [],
|
groups: [],
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
group: {name:'', description:''},
|
group: {name: '', description: ''},
|
||||||
sortColumn: '',
|
sortColumn: '',
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
elementsPerPage: 5,
|
elementsPerPage: 5,
|
||||||
@@ -71,7 +74,7 @@
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
// const { data } = await GroupRepository.get();
|
// const { data } = await GroupRepository.get();
|
||||||
GroupRepository.getGroups()
|
GroupRepository.getGroups()
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
this.groups = response.data;
|
this.groups = response.data;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.$log.debug(response);
|
this.$log.debug(response);
|
||||||
@@ -79,8 +82,8 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
get_group_rows() {
|
get_group_rows() {
|
||||||
var start = (this.currentPage-1) * this.elementsPerPage;
|
const start = (this.currentPage - 1) * this.elementsPerPage;
|
||||||
var end = start + this.elementsPerPage;
|
const end = start + this.elementsPerPage;
|
||||||
return this.groups.slice(start, end);
|
return this.groups.slice(start, end);
|
||||||
},
|
},
|
||||||
num_group_pages() {
|
num_group_pages() {
|
||||||
@@ -97,19 +100,19 @@
|
|||||||
this.sortColumn = col;
|
this.sortColumn = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ascending = this.ascending;
|
const ascending = this.ascending;
|
||||||
|
|
||||||
this.groups.sort(function(a, b) {
|
this.groups.sort((a, b) => {
|
||||||
if (a[col] > b[col]) {
|
if (a[col] > b[col]) {
|
||||||
return ascending ? 1 : -1
|
return ascending ? 1 : -1;
|
||||||
} else if (a[col] < b[col]) {
|
} else if (a[col] < b[col]) {
|
||||||
return ascending ? -1 : 1
|
return ascending ? -1 : 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
createGroup(){
|
createGroup() {
|
||||||
this.$log.info("Creating new group...");
|
this.$log.info('Creating new group...');
|
||||||
GroupRepository.createGroup(this.group);
|
GroupRepository.createGroup(this.group);
|
||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
@@ -118,6 +121,7 @@
|
|||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
// todo....or not
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
group_columns() {
|
group_columns() {
|
||||||
@@ -127,7 +131,7 @@
|
|||||||
return Object.keys(this.groups[0]);
|
return Object.keys(this.groups[0]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -135,6 +139,7 @@
|
|||||||
color: red;
|
color: red;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
font-family: 'Open Sans', sans-serif;
|
font-family: 'Open Sans', sans-serif;
|
||||||
width: 750px;
|
width: 750px;
|
||||||
@@ -152,17 +157,21 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table th:hover {
|
table th:hover {
|
||||||
background: #717699;
|
background: #717699;
|
||||||
}
|
}
|
||||||
|
|
||||||
table td {
|
table td {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-right: 2px solid #7D82A8;
|
border-right: 2px solid #7D82A8;
|
||||||
}
|
}
|
||||||
|
|
||||||
table td:last-child {
|
table td:last-child {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tbody tr:nth-child(2n) td {
|
table tbody tr:nth-child(2n) td {
|
||||||
background: #D4D8F9;
|
background: #D4D8F9;
|
||||||
}
|
}
|
||||||
@@ -184,17 +193,21 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table th:hover {
|
table th:hover {
|
||||||
background: #717699;
|
background: #717699;
|
||||||
}
|
}
|
||||||
|
|
||||||
table td {
|
table td {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-right: 2px solid #7D82A8;
|
border-right: 2px solid #7D82A8;
|
||||||
}
|
}
|
||||||
|
|
||||||
table td:last-child {
|
table td:last-child {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tbody tr:nth-child(2n) td {
|
table tbody tr:nth-child(2n) td {
|
||||||
background: #D4D8F9;
|
background: #D4D8F9;
|
||||||
}
|
}
|
||||||
@@ -209,9 +222,11 @@
|
|||||||
.arrow_down {
|
.arrow_down {
|
||||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAAAXNSR0IArs4c6QAAAvlJREFUSA29Vk1PGlEUHQaiiewslpUJiyYs2yb9AyRuJGm7c0VJoFXSX9A0sSZN04ULF12YEBQDhMCuSZOm1FhTiLY2Rky0QPlQBLRUsICoIN/0PCsGyox26NC3eTNn3r3n3TvnvvsE1PkwGo3yUqkkEQqFgw2Mz7lWqwng7ztN06mxsTEv8U0Aam5u7r5EInkplUol/f391wAJCc7nEAgE9Uwmkzo4OPiJMa1Wq6cFs7Ozt0H6RqlUDmJXfPIx+qrX69Ti4mIyHA5r6Wq1egND+j+IyW6QAUoul18XiUTDNHaSyGazKcZtdgk8wqhUKh9o/OMvsVgsfHJy0iWqVrcQNRUMBnd6enqc9MjISAmRP3e73T9al3XnbWNjIw2+KY1Gc3imsNHR0YV4PP5+d3e32h3K316TySQFoX2WyWR2glzIO5fLTSD6IElLNwbqnFpbWyO/96lCoai0cZjN5kfYQAYi5H34fL6cxWIZbya9iJyAhULBHAqFVlMpfsV/fHxMeb3er+Vy+VUzeduzwWC45XA4dlD/vEXvdDrj8DvURsYEWK3WF4FA4JQP9mg0WrHZbEYmnpa0NxYgPVObm5teiLABdTQT8a6vrwdRWhOcHMzMzCiXlpb2/yV6qDttMpkeshEzRk4Wo/bfoe4X9vb2amzGl+HoXNT29vZqsVi0sK1jJScG+Xx+HGkL4Tew2TPi5zUdQQt9otPpuBk3e0TaHmMDh1zS7/f780S0zX6Yni+NnBj09fUZUfvudDrNZN+GkQbl8Xi8RLRtHzsB9Hr9nfn5+SjSeWUCXC7XPq5kw53wsNogjZNohYXL2EljstvtrAL70/mVaW8Y4OidRO1/gwgbUMvcqGmcDc9aPvD1gnTeQ+0nmaInokRj0nHh+uvIiVOtVvt2a2vLv7Ky0tL3cRTXIcpPAwMDpq6R4/JXE4vFQ5FI5CN+QTaRSFCYc8vLy1l0rge4ARe5kJ/d27kYkLXoy2Jo4C7K8CZOsEBvb+9rlUp1xNXPL7v3IDwxvPD6AAAAAElFTkSuQmCC')
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAAAXNSR0IArs4c6QAAAvlJREFUSA29Vk1PGlEUHQaiiewslpUJiyYs2yb9AyRuJGm7c0VJoFXSX9A0sSZN04ULF12YEBQDhMCuSZOm1FhTiLY2Rky0QPlQBLRUsICoIN/0PCsGyox26NC3eTNn3r3n3TvnvvsE1PkwGo3yUqkkEQqFgw2Mz7lWqwng7ztN06mxsTEv8U0Aam5u7r5EInkplUol/f391wAJCc7nEAgE9Uwmkzo4OPiJMa1Wq6cFs7Ozt0H6RqlUDmJXfPIx+qrX69Ti4mIyHA5r6Wq1egND+j+IyW6QAUoul18XiUTDNHaSyGazKcZtdgk8wqhUKh9o/OMvsVgsfHJy0iWqVrcQNRUMBnd6enqc9MjISAmRP3e73T9al3XnbWNjIw2+KY1Gc3imsNHR0YV4PP5+d3e32h3K316TySQFoX2WyWR2glzIO5fLTSD6IElLNwbqnFpbWyO/96lCoai0cZjN5kfYQAYi5H34fL6cxWIZbya9iJyAhULBHAqFVlMpfsV/fHxMeb3er+Vy+VUzeduzwWC45XA4dlD/vEXvdDrj8DvURsYEWK3WF4FA4JQP9mg0WrHZbEYmnpa0NxYgPVObm5teiLABdTQT8a6vrwdRWhOcHMzMzCiXlpb2/yV6qDttMpkeshEzRk4Wo/bfoe4X9vb2amzGl+HoXNT29vZqsVi0sK1jJScG+Xx+HGkL4Tew2TPi5zUdQQt9otPpuBk3e0TaHmMDh1zS7/f780S0zX6Yni+NnBj09fUZUfvudDrNZN+GkQbl8Xi8RLRtHzsB9Hr9nfn5+SjSeWUCXC7XPq5kw53wsNogjZNohYXL2EljstvtrAL70/mVaW8Y4OidRO1/gwgbUMvcqGmcDc9aPvD1gnTeQ+0nmaInokRj0nHh+uvIiVOtVvt2a2vLv7Ky0tL3cRTXIcpPAwMDpq6R4/JXE4vFQ5FI5CN+QTaRSFCYc8vLy1l0rge4ARe5kJ/d27kYkLXoy2Jo4C7K8CZOsEBvb+9rlUp1xNXPL7v3IDwxvPD6AAAAAElFTkSuQmCC')
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow_up {
|
.arrow_up {
|
||||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAaCAYAAACgoey0AAAAAXNSR0IArs4c6QAAAwpJREFUSA21Vt1PUmEYP4dvkQ8JFMwtBRocWAkDbiqXrUWXzU1rrTt0bdVqXbb1tbW16C9IBUSmm27cODdneoXjputa6069qwuW6IIBIdLvdaF4OAcOiGeDc87zPs/vd57P96WpFq7p6enbGo1mjKZpeTabjU1MTCRagGnOZHFxcXxtbe1XKpUq7+zslJeXl//Mz8+Hy+Uy3RxSE9qTk5M3otFooVQqgef4Wl9f343FYoEmoISrxuNxFX5f9vb2jhn/PxUKhfLS0tIPfFifUESRUMV8Pv/M6XReRm5rTGQyGeXxeGxYe1ezeBpBOBx2rKysbO7v79d4Wy3Y2Nj4GQqFbgnhaugxwiuGJx99Pp9FLBbXxYTXvTqd7v3MzIy6riIWGxJnMpl7AwMD14xGYyMsSq1WUyQdUqn0eSPlusQIsbGrq+vl4OCgvhFQZd1utyv1en0gEolcqsi47nWJlUrlG5fLZVcoFFy2nDKSDpIWlUoVTCQSEk4lCHmJMZ2GTCbTiMVikfIZ88l7enoos9l8dXt7+z6fDicxSJUokqDX6xXcl2wCROoc0vQCWL3sNfLOSdzR0fHY4XC4tVotl40gmVwup9xuN4OQv+UyqCFGH9rg7SOGYVRcBs3IEG4J0nVnamrqOtvuBDGGgQg9+wHFcVEi4a0LNkbdd6TrPKo8ODc311mteIIYjT/a398/jK+s1jnVM0kXoufCFvq0GuiIGEVgQIhfoygM1QrteEa9dAL7ITiYCt4RMabOK5AyKKzKWtvupLcRciu8D5J0EuDDPyT/Snd39yh6VtY2NhYQSR9G79Ds7OxdskRjEyAufvb7/cPoO5Z6e1+xtVKrq6vfcFzyi/A3ZrPZ3GdNSlwgo5ekE4X2RIQGf2C1WlufFE0GBeGWYQ8YERWLxQtnUVB830MKLZfL9RHir8lkssCn2G751tZWEWe03zTKm15YWPiEiXXTYDB0Ig/t7yd8PRws4EicwWHxO4jHD8/C5HiTTqd1BwcHFozKU89origB+y/kmzgYpgOBQP4fGmUiZmJ+WNgAAAAASUVORK5CYII=')
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAaCAYAAACgoey0AAAAAXNSR0IArs4c6QAAAwpJREFUSA21Vt1PUmEYP4dvkQ8JFMwtBRocWAkDbiqXrUWXzU1rrTt0bdVqXbb1tbW16C9IBUSmm27cODdneoXjputa6069qwuW6IIBIdLvdaF4OAcOiGeDc87zPs/vd57P96WpFq7p6enbGo1mjKZpeTabjU1MTCRagGnOZHFxcXxtbe1XKpUq7+zslJeXl//Mz8+Hy+Uy3RxSE9qTk5M3otFooVQqgef4Wl9f343FYoEmoISrxuNxFX5f9vb2jhn/PxUKhfLS0tIPfFifUESRUMV8Pv/M6XReRm5rTGQyGeXxeGxYe1ezeBpBOBx2rKysbO7v79d4Wy3Y2Nj4GQqFbgnhaugxwiuGJx99Pp9FLBbXxYTXvTqd7v3MzIy6riIWGxJnMpl7AwMD14xGYyMsSq1WUyQdUqn0eSPlusQIsbGrq+vl4OCgvhFQZd1utyv1en0gEolcqsi47nWJlUrlG5fLZVcoFFy2nDKSDpIWlUoVTCQSEk4lCHmJMZ2GTCbTiMVikfIZ88l7enoos9l8dXt7+z6fDicxSJUokqDX6xXcl2wCROoc0vQCWL3sNfLOSdzR0fHY4XC4tVotl40gmVwup9xuN4OQv+UyqCFGH9rg7SOGYVRcBs3IEG4J0nVnamrqOtvuBDGGgQg9+wHFcVEi4a0LNkbdd6TrPKo8ODc311mteIIYjT/a398/jK+s1jnVM0kXoufCFvq0GuiIGEVgQIhfoygM1QrteEa9dAL7ITiYCt4RMabOK5AyKKzKWtvupLcRciu8D5J0EuDDPyT/Snd39yh6VtY2NhYQSR9G79Ds7OxdskRjEyAufvb7/cPoO5Z6e1+xtVKrq6vfcFzyi/A3ZrPZ3GdNSlwgo5ekE4X2RIQGf2C1WlufFE0GBeGWYQ8YERWLxQtnUVB830MKLZfL9RHir8lkssCn2G751tZWEWe03zTKm15YWPiEiXXTYDB0Ig/t7yd8PRws4EicwWHxO4jHD8/C5HiTTqd1BwcHFozKU89origB+y/kmzgYpgOBQP4fGmUiZmJ+WNgAAAAASUVORK5CYII=')
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow {
|
.arrow {
|
||||||
float: right;
|
float: right;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
@@ -230,6 +245,7 @@
|
|||||||
margin: 0px 5px;
|
margin: 0px 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.number:hover, .number.active {
|
.number:hover, .number.active {
|
||||||
background: #717699;
|
background: #717699;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(provider, index) in loginProviders" v-bind:id="index">
|
<li v-for="(provider, index) in loginProviders" v-bind:id="index">
|
||||||
<a :href="provider.url">{{ index }} ({{provider.type}})</a>
|
<a :href="provider.url">{{ index }} ({{provider.type}})</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Users</h3>
|
<h3>Users</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(user) in users" >
|
<li v-for="(user) in users">
|
||||||
<a href="#">({{user.id}}) {{user.first_name}}</a>
|
<a href="#">({{user.id}}) {{user.first_name}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -50,10 +50,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { EventBus } from '@/utils'
|
import {EventBus} from '@/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
@@ -62,19 +63,20 @@
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
authenticate () {
|
authenticate() {
|
||||||
this.$store.dispatch('login', { email: this.email, password: this.password })
|
this.$store.dispatch('login', {email: this.email, password: this.password})
|
||||||
.then(() => this.$router.push('/'));
|
.then(() => this.$router.push('/'));
|
||||||
},
|
},
|
||||||
register () {
|
register() {
|
||||||
this.$store.dispatch('register', { email: this.email, password: this.password })
|
this.$store.dispatch('register', {email: this.email, password: this.password})
|
||||||
.then(() => this.$router.push('/'));
|
.then(() => this.$router.push('/'));
|
||||||
},
|
},
|
||||||
oidc_login () {
|
oidc_login() {
|
||||||
this.$store.dispatch('oidc_login', "\\oidc_login_redirection");
|
this.$store.dispatch('oidc_login', '\\oidc_login_redirection');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
|
// this.$parent.$data.isLoading = true;
|
||||||
EventBus.$on('failedRegistering', (msg) => {
|
EventBus.$on('failedRegistering', (msg) => {
|
||||||
this.errorMsg = msg;
|
this.errorMsg = msg;
|
||||||
});
|
});
|
||||||
@@ -91,22 +93,22 @@
|
|||||||
this.$store.dispatch('loadLoginProviders');
|
this.$store.dispatch('loadLoginProviders');
|
||||||
|
|
||||||
// get tokens
|
// get tokens
|
||||||
if(this.$cookies.isKey("tokens")){
|
if (this.$cookies.isKey('tokens')) {
|
||||||
this.$store.dispatch('storeTokens', JSON.parse(atob(this.$cookies.get("tokens"))));
|
this.$store.dispatch('storeTokens', JSON.parse(atob(this.$cookies.get('tokens'))));
|
||||||
this.$cookies.remove("tokens");
|
this.$cookies.remove('tokens');
|
||||||
}
|
}
|
||||||
console.log(this.$cookies.keys());
|
this.$log.debug(this.$cookies.keys());
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
EventBus.$off('failedRegistering');
|
EventBus.$off('failedRegistering');
|
||||||
EventBus.$off('failedAuthentication');
|
EventBus.$off('failedAuthentication');
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
users () {
|
users() {
|
||||||
return this.$store.state.users;
|
return this.$store.state.users;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label is-large">Question</label>
|
|
||||||
<div class="control">
|
|
||||||
<input type="text" class="input is-large" v-model="question">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
<a class="button is-large is-info" @click="addChoice">
|
|
||||||
<span class="icon is-small">
|
|
||||||
<i class="fa fa-plus-square-o fa-align-left" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
<span>Add choice</span>
|
|
||||||
</a>
|
|
||||||
<a class="button is-large is-primary" @click="saveQuestion">
|
|
||||||
<span class="icon is-small">
|
|
||||||
<i class="fa fa-check"></i>
|
|
||||||
</span>
|
|
||||||
<span>Save</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="label is-large" v-show="choices.length > 0">Question Choices</h2>
|
|
||||||
<div class="field has-addons" v-for="(choice, idx) in choices" v-bind:key="idx">
|
|
||||||
<div class="control choice">
|
|
||||||
<input type="text" class="input is-large" v-model="choices[idx]">
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<a class="button is-large">
|
|
||||||
<span class="icon is-small" @click.stop="removeChoice(choice)">
|
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
question: '',
|
|
||||||
choices: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
removeChoice (choice) {
|
|
||||||
const idx = this.choices.findIndex(c => c === choice)
|
|
||||||
this.choices.splice(idx, 1)
|
|
||||||
},
|
|
||||||
saveQuestion () {
|
|
||||||
this.$emit('questionComplete', {
|
|
||||||
question: this.question,
|
|
||||||
choices: this.choices.filter(c => !!c)
|
|
||||||
})
|
|
||||||
this.question = ''
|
|
||||||
this.choices = ['']
|
|
||||||
},
|
|
||||||
addChoice () {
|
|
||||||
this.choices.push('')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.choice {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<section class="hero is-primary">
|
|
||||||
<div class="hero-body">
|
|
||||||
<div class="container has-text-centered">
|
|
||||||
<h2 class="title">{{ name }}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="tabs is-centered is-fullwidth is-large">
|
|
||||||
<ul>
|
|
||||||
<li :class="{'is-active': step == 'name'}" @click="step = 'name'">
|
|
||||||
<a>Name</a>
|
|
||||||
</li>
|
|
||||||
<li :class="{'is-active': step == 'questions'}" @click="step = 'questions'">
|
|
||||||
<a>Questions</a>
|
|
||||||
</li>
|
|
||||||
<li :class="{'is-active': step == 'review'}" @click="step = 'review'">
|
|
||||||
<a>Review</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-half is-offset-one-quarter">
|
|
||||||
|
|
||||||
<div class="name" v-show="step === 'name'">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label is-large" for="name">Survey name:</label>
|
|
||||||
<div class="control">
|
|
||||||
<input type="text" class="input is-large" id="name" v-model="name">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="questions" v-show="step === 'questions'">
|
|
||||||
<new-question v-on:questionComplete="appendQuestion"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="review" v-show="step === 'review'">
|
|
||||||
<ul>
|
|
||||||
<li class="question" v-for="(question, qIdx) in questions" :key="`question-${qIdx}`">
|
|
||||||
<div class="title">
|
|
||||||
{{ question.question }}
|
|
||||||
<span class="icon is-medium is-pulled-right delete-question"
|
|
||||||
@click.stop="removeQuestion(question)">
|
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li v-for="(choice , cIdx) in question.choices" :key="`choice-${cIdx}`">
|
|
||||||
{{ cIdx + 1 }}. {{ choice }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="control">
|
|
||||||
<a class="button is-large is-primary" @click="submitSurvey">Submit</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import NewQuestion from '@/components/NewQuestion'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { NewQuestion },
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
step: 'name',
|
|
||||||
name: '',
|
|
||||||
questions: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
appendQuestion (newQuestion) {
|
|
||||||
this.questions.push(newQuestion)
|
|
||||||
},
|
|
||||||
removeQuestion (question) {
|
|
||||||
const idx = this.questions.findIndex(q => q.question === question.question)
|
|
||||||
this.questions.splice(idx, 1)
|
|
||||||
},
|
|
||||||
submitSurvey () {
|
|
||||||
this.$store.dispatch('submitNewSurvey', {
|
|
||||||
name: this.name,
|
|
||||||
questions: this.questions
|
|
||||||
})
|
|
||||||
.then(() => this.$router.push('/'))
|
|
||||||
.catch((error) => {
|
|
||||||
console.log('Error creating survey', error)
|
|
||||||
this.$router.push('/')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.question {
|
|
||||||
margin: 10px 20px 25px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-question {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-question:hover {
|
|
||||||
background-color: lightgray;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -17,11 +17,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { EventBus } from '@/utils'
|
import {EventBus} from '@/utils';
|
||||||
import {getRemainingJwtValiditySeconds} from "../utils";
|
import {getRemainingJwtValiditySeconds} from '../utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
email: '',
|
email: '',
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
@@ -30,19 +30,19 @@
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
authenticate () {
|
authenticate() {
|
||||||
this.$store.dispatch('login', { email: this.email, password: this.password })
|
this.$store.dispatch('login', {email: this.email, password: this.password})
|
||||||
.then(() => this.$router.push('/'));
|
.then(() => this.$router.push('/'));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
this.$log.debug("Profile: mounting...");
|
this.$log.debug('Profile: mounting...');
|
||||||
EventBus.$on('failedLoadingProfile', (msg) => {
|
EventBus.$on('failedLoadingProfile', (msg) => {
|
||||||
this.errorMsg = msg;
|
this.errorMsg = msg;
|
||||||
});
|
});
|
||||||
this.$store.dispatch('loadProfile');
|
this.$store.dispatch('loadProfile');
|
||||||
|
|
||||||
this.$nextTick(() =>{
|
this.$nextTick( () => {
|
||||||
window.setInterval(() => {
|
window.setInterval(() => {
|
||||||
this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
|
this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
|
||||||
this.tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
|
this.tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
|
||||||
@@ -52,15 +52,15 @@
|
|||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
EventBus.$off('failedLoadingProfile');
|
EventBus.$off('failedLoadingProfile');
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
profile () {
|
profile() {
|
||||||
return this.$store.state.profile;
|
return this.$store.state.profile;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
271
src/components/Recorders.vue
Normal file
271
src/components/Recorders.vue
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<section class="section">
|
||||||
|
<h1 class="title">{{ $t('Manage recorders and recorder models') }}</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
{{ $t('List, create, update and delete') }} <strong>{{ $t('recorders') }}</strong> and <strong>recorder models</strong>!
|
||||||
|
</p>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h2 class="">{{ $t('Create recorder') }}</h2>
|
||||||
|
<!-- form starts here -->
|
||||||
|
<form v-on:submit.prevent="$log.debug(form)">
|
||||||
|
<section class="form">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('name') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input name="name"
|
||||||
|
v-model="form.name"
|
||||||
|
v-validate="'required|min:3'"
|
||||||
|
v-bind:class="{'is-danger': errors.has('name')}"
|
||||||
|
class="input" type="text" placeholder="Full name">
|
||||||
|
</div>
|
||||||
|
<p class="help is-danger" v-show="errors.has('name')">
|
||||||
|
{{ errors.first('name') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('description') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea class="textarea" placeholder="description of recorder"
|
||||||
|
v-model="form.description"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('room') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select">
|
||||||
|
<select v-model="form.room">
|
||||||
|
<option disabled value="">No room selected</option>
|
||||||
|
<option v-for="option in options.room" v-bind:value="option.value">
|
||||||
|
{{ option.text }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('model') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select">
|
||||||
|
<select v-model="form.model">
|
||||||
|
<option disabled value="">No model selected</option>
|
||||||
|
<option v-for="option in options.model" v-bind:value="option.value">
|
||||||
|
{{ option.text }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">LogRocket Usecases</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-multiple">
|
||||||
|
<select multiple v-model="form.logrocket_usecases">
|
||||||
|
<option>Debugging</option>
|
||||||
|
<option>Fixing Errors</option>
|
||||||
|
<option>User support</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" v-model="form.terms">
|
||||||
|
I agree to the <a href="#">terms and conditions</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label>
|
||||||
|
<strong>What dev concepts are you interested in?</strong>
|
||||||
|
</label>
|
||||||
|
<div class="control">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" v-model="form.concepts"
|
||||||
|
value="promises">
|
||||||
|
Promises
|
||||||
|
</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" v-model="form.concepts"
|
||||||
|
value="testing">
|
||||||
|
Testing
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label><strong>Is JavaScript awesome?</strong></label>
|
||||||
|
<div class="control">
|
||||||
|
<label class="radio">
|
||||||
|
<input v-model="form.js_awesome" type="radio" value="Yes">
|
||||||
|
Yes
|
||||||
|
</label>
|
||||||
|
<label class="radio">
|
||||||
|
<input v-model="form.js_awesome" type="radio" value="Yeap!">
|
||||||
|
Yeap!
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
v-bind:disabled="errors.any()"
|
||||||
|
class="button is-primary">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h2 class="">{{ $t('Create recorder model') }}</h2>
|
||||||
|
<!-- form starts here -->
|
||||||
|
<form v-on:submit.prevent="$log.debug(form)">
|
||||||
|
<section class="form">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('model name') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input name="name"
|
||||||
|
v-model="form.name"
|
||||||
|
v-validate="'required|min:3'"
|
||||||
|
v-bind:class="{'is-danger': errors.has('name')}"
|
||||||
|
class="input" type="text" placeholder="Full name">
|
||||||
|
</div>
|
||||||
|
<p class="help is-danger" v-show="errors.has('name')">
|
||||||
|
{{ errors.first('name') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('notes') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea class="textarea" placeholder="Notes"
|
||||||
|
v-model="form.notes"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Recorder commands</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="select is-multiple">
|
||||||
|
<select multiple v-model="form.commands">
|
||||||
|
<option v-for="option in recorderCommands.options">
|
||||||
|
{{ option }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
v-bind:disabled="errors.any()"
|
||||||
|
class="button is-primary">
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<section class="section" id="results">
|
||||||
|
<div class="box">
|
||||||
|
<ul>
|
||||||
|
<!-- loop through all the `form` properties and show their values -->
|
||||||
|
<li v-for="(item, k) in form">
|
||||||
|
<strong>{{ k }}:</strong> {{ item }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
step: 'name',
|
||||||
|
name: '',
|
||||||
|
questions: [],
|
||||||
|
form: {
|
||||||
|
name: '',
|
||||||
|
message: '',
|
||||||
|
inquiry_type: '',
|
||||||
|
logrocket_usecases: [],
|
||||||
|
terms: false,
|
||||||
|
concepts: [],
|
||||||
|
js_awesome: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
appendQuestion(newQuestion) {
|
||||||
|
this.questions.push(newQuestion);
|
||||||
|
},
|
||||||
|
removeQuestion(question) {
|
||||||
|
const idx = this.questions.findIndex((q) => q.question === question.question);
|
||||||
|
this.questions.splice(idx, 1);
|
||||||
|
},
|
||||||
|
submitSurvey() {
|
||||||
|
this.$store.dispatch('submitNewSurvey', {
|
||||||
|
name: this.name,
|
||||||
|
questions: this.questions,
|
||||||
|
})
|
||||||
|
.then(() => this.$router.push('/'))
|
||||||
|
.catch((error) => {
|
||||||
|
this.$log.error('Error creating survey', error);
|
||||||
|
this.$router.push('/');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
options() {
|
||||||
|
return {
|
||||||
|
room: [
|
||||||
|
{value: 'audimax', text: 'AudiMax'},
|
||||||
|
{value: 'hmu', text: 'Unterer Hörsaal'},
|
||||||
|
{value: 'hmo', text: 'Oberer Hörsaal'},
|
||||||
|
],
|
||||||
|
model: [
|
||||||
|
{value: 'extron', text: 'extron'},
|
||||||
|
{value: 'smp', text: 'SMP'},
|
||||||
|
{value: 'smp2', text: 'SMP2'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
recorderCommands() {
|
||||||
|
return {
|
||||||
|
options: [
|
||||||
|
'reboot',
|
||||||
|
'reset',
|
||||||
|
'setStreamingProfile',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
353
src/components/Rooms.vue
Normal file
353
src/components/Rooms.vue
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<section class="section">
|
||||||
|
|
||||||
|
|
||||||
|
<h1 class="title">{{ $t('Manage rooms') }}</h1>
|
||||||
|
<p class="lead">
|
||||||
|
{{ $t('List, create and delete') }} <strong>{{ $t('rooms') }}</strong>
|
||||||
|
</p>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" id="room-list-tab" data-toggle="tab" href="#room-list" role="tab"
|
||||||
|
aria-controls="room-list" aria-selected="true">Room list</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="room-create-tab" data-toggle="tab" href="#room-create" role="tab"
|
||||||
|
aria-controls="room-create" aria-selected="false">Create room</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<b-tabs card>
|
||||||
|
<b-tab title="Room list" active>
|
||||||
|
<p>{{ $t('There are')}} {{rooms.length}} {{ $t('rooms defined')}}:</p>
|
||||||
|
<b-card-group deck>
|
||||||
|
<b-card v-for="(room) in rooms" :title="room.name">
|
||||||
|
<b-card-text>
|
||||||
|
<h5 class="card-title">{{ $t('name') }}: {{room.name}}
|
||||||
|
<a class="float-right badge badge-pill badge-primary">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="card-text"><strong>{{ $t('alternate_name') }}:</strong> {{room.alternate_name}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="card-text"><strong>{{ $t('room_number') }}:</strong> {{room.number}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a></p>
|
||||||
|
|
||||||
|
<p class="card-text">
|
||||||
|
<small><strong>{{ $t('comment') }}:</strong> {{room.comment}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a></small>
|
||||||
|
</p>
|
||||||
|
<hr/>
|
||||||
|
<div v-if="room.recorder">
|
||||||
|
<p class="card-text"><strong>{{ $t('Recorder') }}:</strong> {{room.recorder.name}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a></p>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p class="card-text"><strong>{{ $t('Recorder') }}:</strong> {{
|
||||||
|
$t('no_recorder_defined')}}</p>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select class="form-control" v-model="form.recorder">
|
||||||
|
<option disabled value="">No recorder selected</option>
|
||||||
|
<option v-for="option in options.recorders" v-bind:value="option.value">
|
||||||
|
{{ option.text }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="label col-sm-4 col-form-label">{{ $t('recorder') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox"
|
||||||
|
v-model="show_assigned_recorders" id="defaultCheck1">
|
||||||
|
<label class="form-check-label" for="defaultCheck1">
|
||||||
|
Show already assigned recorders
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-card-text>
|
||||||
|
<div slot="footer">
|
||||||
|
<small class="text-muted">Last updated 3 mins ago</small>
|
||||||
|
</div>
|
||||||
|
</b-card>
|
||||||
|
|
||||||
|
<b-card title="Title">
|
||||||
|
<b-card-text>
|
||||||
|
This card has supporting text below as a natural lead-in to additional content.
|
||||||
|
</b-card-text>
|
||||||
|
<div slot="footer">
|
||||||
|
<small class="text-muted">Last updated 3 mins ago</small>
|
||||||
|
</div>
|
||||||
|
</b-card>
|
||||||
|
|
||||||
|
<b-card title="Title">
|
||||||
|
<b-card-text>
|
||||||
|
This is a wider card with supporting text below as a natural lead-in to additional
|
||||||
|
content.
|
||||||
|
This card has even longer content than the first to show that equal height action.
|
||||||
|
</b-card-text>
|
||||||
|
<div slot="footer">
|
||||||
|
<small class="text-muted">Last updated 3 mins ago</small>
|
||||||
|
</div>
|
||||||
|
</b-card>
|
||||||
|
</b-card-group>
|
||||||
|
</b-tab>
|
||||||
|
<b-tab title="Create room">
|
||||||
|
<b-card-text>
|
||||||
|
<p>{{ $t('Create a new room')}}:</p>
|
||||||
|
</b-card-text>
|
||||||
|
</b-tab>
|
||||||
|
</b-tabs>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="tab-content" id="myTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="room-list" role="tabpanel" aria-labelledby="home-tab">
|
||||||
|
<p>{{ $t('There are')}} {{rooms.length}} {{ $t('rooms defined')}}:</p>
|
||||||
|
<div class="card-deck">
|
||||||
|
<div class="card" v-for="(room) in rooms">
|
||||||
|
<h5 class="card-header">{{room.name}} - {{room.number}}</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{ $t('name') }}: {{room.name}}
|
||||||
|
<a class="float-right badge badge-pill badge-primary">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="card-text"><strong>{{ $t('alternate_name') }}:</strong> {{room.alternate_name}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="card-text"><strong>{{ $t('room_number') }}:</strong> {{room.number}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a></p>
|
||||||
|
|
||||||
|
<p class="card-text">
|
||||||
|
<small><strong>{{ $t('comment') }}:</strong> {{room.comment}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a></small>
|
||||||
|
</p>
|
||||||
|
<hr/>
|
||||||
|
<div v-if="room.recorder">
|
||||||
|
<p class="card-text"><strong>{{ $t('Recorder') }}:</strong> {{room.recorder.name}}
|
||||||
|
<a class="float-right badge badge-pill badge-info">
|
||||||
|
<font-awesome-icon icon="pencil-alt"/>
|
||||||
|
</a></p>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p class="card-text"><strong>{{ $t('Recorder') }}:</strong> {{
|
||||||
|
$t('no_recorder_defined')}}</p>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select class="form-control" v-model="form.recorder">
|
||||||
|
<option disabled value="">No recorder selected</option>
|
||||||
|
<option v-for="option in options.recorders" v-bind:value="option.value">
|
||||||
|
{{ option.text }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="label col-sm-4 col-form-label">{{ $t('recorder') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox"
|
||||||
|
v-model="show_assigned_recorders" id="defaultCheck1">
|
||||||
|
<label class="form-check-label" for="defaultCheck1">
|
||||||
|
Show already assigned recorders
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-muted">
|
||||||
|
<button type="button" class="btn btn-danger">{{ $t('delete') }} <font-awesome-icon
|
||||||
|
icon="trash"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="room-create" role="tabpanel" aria-labelledby="profile-tab">
|
||||||
|
<p>{{ $t('Create a new room')}}:</p>
|
||||||
|
<!-- form starts here -->
|
||||||
|
<form v-on:submit.prevent="saveRoom()">
|
||||||
|
<section class="form">
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="label required col-sm-2 col-form-label">{{ $t('name') }}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input name="name"
|
||||||
|
v-model="form.name"
|
||||||
|
v-validate="'required|min:3'"
|
||||||
|
v-bind:class="{'is-danger': errors.has('name'), 'is-invalid': errors.has('name')}"
|
||||||
|
class="form-control" type="text" placeholder="Room name" required>
|
||||||
|
</div>
|
||||||
|
<p class="col-sm-4" v-show="errors.has('name')">
|
||||||
|
{{ errors.first('name') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="label col-sm-2 col-form-label">{{ $t('alternate_name') }}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input name="alternate_name"
|
||||||
|
v-model="form.alternate_name"
|
||||||
|
class="form-control" type="text" placeholder="Alternate name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="label required col-sm-2 col-form-label">{{ $t('number') }}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input name="number"
|
||||||
|
v-model="form.number"
|
||||||
|
v-validate="'required|min:3'"
|
||||||
|
v-bind:class="{'is-danger': errors.has('number'), 'is-invalid': errors.has('number')}"
|
||||||
|
class="form-control" type="text" placeholder="Room number" required>
|
||||||
|
</div>
|
||||||
|
<p class="col-sm-4" v-show="errors.has('number')">
|
||||||
|
{{ errors.first('number') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="label col-sm-2 col-form-label">{{ $t('comment') }}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<textarea class="textarea form-control" placeholder="Comments, remarks, notes, etc."
|
||||||
|
v-model="form.comment"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="label col-sm-2 col-form-label">{{ $t('recorder') }}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<select class="form-control" v-model="form.recorder">
|
||||||
|
<option disabled value="">No recorder selected</option>
|
||||||
|
<option v-for="option in options.recorders" v-bind:value="option.value">
|
||||||
|
{{ option.text }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
v-bind:disabled="errors.any()"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary">
|
||||||
|
Create room
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<section class="section" id="results">
|
||||||
|
<div class="box">
|
||||||
|
<ul>
|
||||||
|
<!-- loop through all the `form` properties and show their values -->
|
||||||
|
<li v-for="(item, k) in form">
|
||||||
|
<strong>{{ k }}:</strong> {{ item }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PulseLoader from 'vue-spinner/src/PulseLoader.vue';
|
||||||
|
import getRepository from '@/api/RepositoryFactory';
|
||||||
|
|
||||||
|
const RoomRepository = getRepository('room');
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PulseLoader,
|
||||||
|
},
|
||||||
|
props: [],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show_assigned_recorders: false,
|
||||||
|
form: {
|
||||||
|
name: '',
|
||||||
|
alternate_name: '',
|
||||||
|
number: '',
|
||||||
|
comment: '',
|
||||||
|
recorder: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveRoom() {
|
||||||
|
this.$parent.$data.isLoading = true;
|
||||||
|
RoomRepository.createRoom(this.form)
|
||||||
|
.then(() => {
|
||||||
|
this.$store.dispatch('loadRooms')
|
||||||
|
.then(() => {
|
||||||
|
this.$parent.$data.isLoading = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$parent.$data.isLoading = true;
|
||||||
|
this.$store.dispatch('loadRooms')
|
||||||
|
.then(() => {
|
||||||
|
this.$parent.$data.isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
options() {
|
||||||
|
return {
|
||||||
|
recorders: [
|
||||||
|
{value: 8, text: 'SMP 351 AudiMax'},
|
||||||
|
{value: 13, text: 'SMP 351 HMU'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
rooms() {
|
||||||
|
return this.$store.state.rooms;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.comment {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
background-color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required:after {
|
||||||
|
content: " *";
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<section class="hero is-primary">
|
|
||||||
<div class="hero-body">
|
|
||||||
<div class="container has-text-centered">
|
|
||||||
<h2 class="title">{{ survey.name }}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-10 is-offset-1">
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-for="(question, idx) in survey.questions"
|
|
||||||
v-bind:key="question.id"
|
|
||||||
v-show="currentQuestion === idx">
|
|
||||||
|
|
||||||
<div class="column is-offset-3 is-6">
|
|
||||||
<h4 class='title has-text-centered'>{{ question.text }}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="column is-offset-4 is-4">
|
|
||||||
<div class="control">
|
|
||||||
<div v-for="choice in question.choices" v-bind:key="choice.id">
|
|
||||||
<label class="radio">
|
|
||||||
<input type="radio" v-model="question.choice" name="choice" :value="choice.id">
|
|
||||||
{{ choice.text }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column is-offset-one-quarter is-half">
|
|
||||||
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
|
|
||||||
<a class="pagination-previous" @click.stop="goToPreviousQuestion"><i class="fa fa-chevron-left" aria-hidden="true"></i> Back</a>
|
|
||||||
<a class="pagination-next" @click.stop="goToNextQuestion">Next <i class="fa fa-chevron-right" aria-hidden="true"></i></a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="has-text-centered">
|
|
||||||
<a v-show="surveyComplete" class='button is-large is-focused is-primary' @click="handleSubmit">Submit</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
currentQuestion: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeMount () {
|
|
||||||
this.$store.dispatch('loadSurvey', { id: parseInt(this.$route.params.id) })
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
goToNextQuestion () {
|
|
||||||
if (this.currentQuestion === this.survey.questions.length - 1) {
|
|
||||||
this.currentQuestion = 0
|
|
||||||
} else {
|
|
||||||
this.currentQuestion++
|
|
||||||
}
|
|
||||||
},
|
|
||||||
goToPreviousQuestion () {
|
|
||||||
if (this.currentQuestion === 0) {
|
|
||||||
this.currentQuestion = this.survey.questions.lenth - 1
|
|
||||||
} else {
|
|
||||||
this.currentQuestion--
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleSubmit () {
|
|
||||||
this.$store.dispatch('addSurveyResponse')
|
|
||||||
.then(() => this.$router.push('/'))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
surveyComplete () {
|
|
||||||
if (this.survey.questions) {
|
|
||||||
const numQuestions = this.survey.questions.length
|
|
||||||
const numCompleted = this.survey.questions.filter(q => q.choice).length
|
|
||||||
return numQuestions === numCompleted
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
survey () {
|
|
||||||
return this.$store.state.currentSurvey
|
|
||||||
},
|
|
||||||
selectedChoice: {
|
|
||||||
get () {
|
|
||||||
const question = this.survey.questions[this.currentQuestion]
|
|
||||||
return question.choice
|
|
||||||
},
|
|
||||||
set (value) {
|
|
||||||
const question = this.survey.questions[this.currentQuestion]
|
|
||||||
this.$store.commit('setChoice', { questionId: question.id, choice: value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
@@ -5,14 +5,15 @@
|
|||||||
<div class="hero-body">
|
<div class="hero-body">
|
||||||
<div class="container has-text-centered">
|
<div class="container has-text-centered">
|
||||||
<h3 class="title">Manage users</h3>
|
<h3 class="title">Manage users</h3>
|
||||||
<div>{{users}}</div>
|
<div>{{users}}</div>
|
||||||
|
|
||||||
<div id="userTable">
|
<div id="userTable">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="col in columns" v-on:click="sortTable(col)">{{col}}
|
<th v-for="col in columns" v-on:click="sortTable(col)">{{col}}
|
||||||
<div class="arrow" v-if="col == sortColumn" v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
|
<div class="arrow" v-if="col == sortColumn"
|
||||||
|
v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -26,7 +27,8 @@
|
|||||||
<div class="number"
|
<div class="number"
|
||||||
v-for="i in num_pages()"
|
v-for="i in num_pages()"
|
||||||
v-bind:class="[i === currentPage ? 'active' : '']"
|
v-bind:class="[i === currentPage ? 'active' : '']"
|
||||||
v-on:click="change_page(i)">{{i}}</div>
|
v-on:click="change_page(i)">{{i}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -47,14 +49,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { EventBus } from '@/utils';
|
import {EventBus} from '@/utils';
|
||||||
import {getRemainingJwtValiditySeconds} from '../utils';
|
import {getRemainingJwtValiditySeconds} from '../utils';
|
||||||
import { RepositoryFactory} from '@/api/RepositoryFactory';
|
import getRepository from '@/api/RepositoryFactory';
|
||||||
const UserRepository = RepositoryFactory.get('user');
|
|
||||||
|
const UserRepository = getRepository('user');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
isLoading: false,
|
||||||
user: {},
|
user: {},
|
||||||
users: [],
|
users: [],
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
@@ -68,7 +72,7 @@
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
// const { data } = await GroupRepository.get();
|
// const { data } = await GroupRepository.get();
|
||||||
UserRepository.getUsers()
|
UserRepository.getUsers()
|
||||||
.then( (response) => {
|
.then((response) => {
|
||||||
this.users = response.data;
|
this.users = response.data;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.$log.debug(response);
|
this.$log.debug(response);
|
||||||
@@ -76,8 +80,8 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
get_rows() {
|
get_rows() {
|
||||||
let start = (this.currentPage-1) * this.elementsPerPage;
|
const start = (this.currentPage - 1) * this.elementsPerPage;
|
||||||
let end = start + this.elementsPerPage;
|
const end = start + this.elementsPerPage;
|
||||||
return this.users.slice(start, end);
|
return this.users.slice(start, end);
|
||||||
},
|
},
|
||||||
num_pages() {
|
num_pages() {
|
||||||
@@ -94,19 +98,19 @@
|
|||||||
this.sortColumn = col;
|
this.sortColumn = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ascending = this.ascending;
|
const ascending = this.ascending;
|
||||||
|
|
||||||
this.users.sort(function(a, b) {
|
this.users.sort((a, b) => {
|
||||||
if (a[col] > b[col]) {
|
if (a[col] > b[col]) {
|
||||||
return ascending ? 1 : -1
|
return ascending ? 1 : -1;
|
||||||
} else if (a[col] < b[col]) {
|
} else if (a[col] < b[col]) {
|
||||||
return ascending ? -1 : 1
|
return ascending ? -1 : 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
createUser(){
|
createUser() {
|
||||||
this.$log.info("Creating new user...");
|
this.$log.info('Creating new user...');
|
||||||
UserRepository.createUser(this.user);
|
UserRepository.createUser(this.user);
|
||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
@@ -115,6 +119,7 @@
|
|||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
// todo
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
columns() {
|
columns() {
|
||||||
@@ -124,7 +129,7 @@
|
|||||||
return Object.keys(this.users[0]);
|
return Object.keys(this.users[0]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
20
src/main.ts
20
src/main.ts
@@ -1,6 +1,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import VueAxios from 'vue-axios';
|
import VueAxios from 'vue-axios';
|
||||||
|
import BootstrapVue from 'bootstrap-vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
@@ -8,16 +9,29 @@ import VueSweetalert2 from 'vue-sweetalert2';
|
|||||||
import VueCookies from 'vue-cookies';
|
import VueCookies from 'vue-cookies';
|
||||||
import VueLogger from 'vuejs-logger';
|
import VueLogger from 'vuejs-logger';
|
||||||
import i18n from '@/plugins/i18n';
|
import i18n from '@/plugins/i18n';
|
||||||
|
import VeeValidate from 'vee-validate';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import FlagIcon from 'vue-flag-icon';
|
import FlagIcon from 'vue-flag-icon';
|
||||||
// following is to avoid missing type definitions
|
// following is to avoid missing type definitions
|
||||||
// const FlagIcon = require('vue-flag-icon');
|
// const FlagIcon = require('vue-flag-icon');
|
||||||
|
|
||||||
import 'bootstrap';
|
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import { faCoffee, faTrash, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
|
||||||
|
|
||||||
|
//import 'bootstrap';
|
||||||
|
//import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
|
||||||
|
import 'bootstrap/dist/css/bootstrap.css'
|
||||||
|
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
library.add(faCoffee, faTrash, faPencilAlt);
|
||||||
|
|
||||||
|
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
// logLevel : isProduction ? 'error' : 'debug',
|
// logLevel : isProduction ? 'error' : 'debug',
|
||||||
@@ -31,9 +45,11 @@ const options = {
|
|||||||
|
|
||||||
Vue.use(VueLogger, options);
|
Vue.use(VueLogger, options);
|
||||||
Vue.use(VueAxios, axios);
|
Vue.use(VueAxios, axios);
|
||||||
|
Vue.use(BootstrapVue);
|
||||||
Vue.use(FlagIcon);
|
Vue.use(FlagIcon);
|
||||||
Vue.use(VueCookies);
|
Vue.use(VueCookies);
|
||||||
Vue.use(VueSweetalert2);
|
Vue.use(VueSweetalert2);
|
||||||
|
Vue.use(VeeValidate);
|
||||||
|
|
||||||
// setup fake backend
|
// setup fake backend
|
||||||
// import { configureFakeBackend } from './helpers';
|
// import { configureFakeBackend } from './helpers';
|
||||||
|
|||||||
165
src/router.ts
165
src/router.ts
@@ -3,97 +3,110 @@ import Router from 'vue-router';
|
|||||||
import Home from './views/Home.vue';
|
import Home from './views/Home.vue';
|
||||||
import NotFound from './views/NotFound.vue';
|
import NotFound from './views/NotFound.vue';
|
||||||
|
|
||||||
import Survey from '@/components/Survey.vue';
|
import NewSurvey from '@/components/Rooms.vue';
|
||||||
import NewSurvey from '@/components/NewSurvey.vue';
|
|
||||||
import Login from '@/components/Login.vue';
|
import Login from '@/components/Login.vue';
|
||||||
import Admin from '@/components/Admin.vue';
|
import Admin from '@/components/Admin.vue';
|
||||||
import Profile from '@/components/Profile.vue';
|
import Profile from '@/components/Profile.vue';
|
||||||
import User from '@/components/User.vue';
|
import User from '@/components/User.vue';
|
||||||
import Group from '@/components/Group.vue';
|
import Group from '@/components/Group.vue';
|
||||||
|
import Rooms from '@/components/Rooms.vue';
|
||||||
|
import Recorders from '@/components/Recorders.vue';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
|
|
||||||
Vue.use(Router);
|
Vue.use(Router);
|
||||||
|
|
||||||
export const router = new Router({
|
export const router = new Router({
|
||||||
// export default new Router({
|
// export default new Router({
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
base: process.env.BASE_URL,
|
base: process.env.BASE_URL,
|
||||||
routes: [
|
routes: [
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
component: Home,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/login',
|
|
||||||
name: 'login',
|
|
||||||
component: Login,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/admin',
|
|
||||||
name: 'admin',
|
|
||||||
component: Admin,
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
name: 'admin.user',
|
path: '/',
|
||||||
path: 'user',
|
name: 'home',
|
||||||
component: User,
|
component: Home,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'admin.group',
|
path: '/login',
|
||||||
path: 'group',
|
name: 'login',
|
||||||
component: Group,
|
component: Login,
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
},
|
path: '/admin',
|
||||||
{
|
name: 'admin',
|
||||||
path: '/about',
|
component: Admin,
|
||||||
name: 'about',
|
children: [
|
||||||
// route level code-splitting
|
{
|
||||||
// this generates a separate chunk (about.[hash].js) for this route
|
name: 'admin.user',
|
||||||
// which is lazy-loaded when the route is visited.
|
path: 'user',
|
||||||
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
|
component: User,
|
||||||
}, {
|
},
|
||||||
path: '/surveys/:id',
|
{
|
||||||
name: 'Survey',
|
name: 'admin.group',
|
||||||
component: Survey,
|
path: 'group',
|
||||||
}, {
|
component: Group,
|
||||||
path: '/surveys',
|
},
|
||||||
name: 'NewSurvey',
|
],
|
||||||
component: NewSurvey,
|
},
|
||||||
beforeEnter(to, from, next) {
|
{
|
||||||
if (!store.getters.isAuthenticated) {
|
path: '/about',
|
||||||
next('/login');
|
name: 'about',
|
||||||
} else {
|
// route level code-splitting
|
||||||
next();
|
// this generates a separate chunk (about.[hash].js) for this route
|
||||||
}
|
// which is lazy-loaded when the route is visited.
|
||||||
},
|
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
|
||||||
}, {
|
}, {
|
||||||
path: '/profile',
|
path: '/surveys/:id',
|
||||||
name: 'Profile',
|
name: 'Survey',
|
||||||
component: Profile,
|
component: Rooms,
|
||||||
beforeEnter(to, from, next) {
|
}, {
|
||||||
if (!store.getters.isAuthenticated) {
|
path: '/rooms',
|
||||||
Vue.$log.debug('not authenticated!');
|
name: 'rooms',
|
||||||
if (store.getters.isRefreshTokenValid) {
|
component: Rooms,
|
||||||
Vue.$log.debug('refresh token is still valid :)');
|
}, {
|
||||||
store.dispatch('refreshToken')
|
path: '/recorders',
|
||||||
.then(() => next())
|
name: 'recorders',
|
||||||
.catch(() => next('/login'));
|
component: Recorders,
|
||||||
} else {
|
}, {
|
||||||
next('/login');
|
path: '/commands',
|
||||||
}
|
name: 'commands',
|
||||||
} else {
|
component: Rooms,
|
||||||
next();
|
}, {
|
||||||
}
|
path: '/surveys',
|
||||||
},
|
name: 'NewSurvey',
|
||||||
},
|
component: NewSurvey,
|
||||||
{
|
beforeEnter(to, from, next) {
|
||||||
path: '*',
|
if (!store.getters.isAuthenticated) {
|
||||||
name: 'notFound',
|
next('/login');
|
||||||
component: NotFound,
|
} else {
|
||||||
},
|
next();
|
||||||
],
|
}
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
path: '/profile',
|
||||||
|
name: 'Profile',
|
||||||
|
component: Profile,
|
||||||
|
beforeEnter(to, from, next) {
|
||||||
|
if (!store.getters.isAuthenticated) {
|
||||||
|
Vue.$log.debug('not authenticated!');
|
||||||
|
if (store.getters.isRefreshTokenValid) {
|
||||||
|
Vue.$log.debug('refresh token is still valid :)');
|
||||||
|
store.dispatch('refreshToken')
|
||||||
|
.then(() => next())
|
||||||
|
.catch(() => next('/login'));
|
||||||
|
} else {
|
||||||
|
next('/login');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
name: 'notFound',
|
||||||
|
component: NotFound,
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
357
src/store.ts
357
src/store.ts
@@ -1,6 +1,8 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import createPersistedState from 'vuex-persistedstate';
|
import createPersistedState from 'vuex-persistedstate';
|
||||||
|
import getRepository from '@/api/RepositoryFactory';
|
||||||
|
import RoomRepository from '@/api/roomRepository';
|
||||||
|
|
||||||
// imports of AJAX functions will go here
|
// imports of AJAX functions will go here
|
||||||
import {
|
import {
|
||||||
@@ -14,13 +16,13 @@ import {
|
|||||||
oidc_login, fetchUsers, getFreshToken, fetchProfile, fetchUserGroups,
|
oidc_login, fetchUsers, getFreshToken, fetchProfile, fetchUserGroups,
|
||||||
} from '@/api';
|
} from '@/api';
|
||||||
import {isValidJwt, EventBus} from '@/utils';
|
import {isValidJwt, EventBus} from '@/utils';
|
||||||
import {response} from "express";
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
// single source of data
|
// single source of data
|
||||||
surveys: [],
|
surveys: [],
|
||||||
|
rooms: [],
|
||||||
loginProviders: [],
|
loginProviders: [],
|
||||||
currentSurvey: {},
|
currentSurvey: {},
|
||||||
profile: {},
|
profile: {},
|
||||||
@@ -30,43 +32,44 @@ const state = {
|
|||||||
refresh_token: '',
|
refresh_token: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// const RoomRepository = getRepository('room');
|
||||||
|
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
// asynchronous operations
|
// asynchronous operations
|
||||||
loadSurveys(context: any) {
|
loadRooms(context: any) {
|
||||||
return fetchSurveys()
|
return RoomRepository.getRooms()
|
||||||
.then((response) => {
|
.then((response: any) => {
|
||||||
context.commit('setSurveys', { surveys: response.data });
|
Vue.$log.debug(response);
|
||||||
});
|
Vue.$log.debug(response.data);
|
||||||
},
|
context.commit('setRooms', {rooms: response.data});
|
||||||
// @ts-ignore
|
EventBus.$emit('roomsLoaded', response.data);
|
||||||
loadSurvey(context: any, { id }) {
|
})
|
||||||
return fetchSurvey(id)
|
.catch((error: any) => {
|
||||||
.then((response) => {
|
Vue.$log.warn('Error loading users!', error);
|
||||||
context.commit('setSurvey', { survey: response.data });
|
EventBus.$emit('failedLoadingUsers', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addSurveyResponse(context: any) {
|
loadUsers(context: any) {
|
||||||
return saveSurveyResponse(context.state.currentSurvey);
|
return fetchUsers(context.state.access_token)
|
||||||
},
|
.then((response) => {
|
||||||
loadUsers(context: any) {
|
Vue.$log.debug(response);
|
||||||
return fetchUsers(context.state.access_token)
|
Vue.$log.debug(response.data);
|
||||||
.then((response) => {
|
context.commit('setUsers', {users: response.data});
|
||||||
Vue.$log.debug(response);
|
EventBus.$emit('usersLoaded', response.data);
|
||||||
Vue.$log.debug(response.data);
|
})
|
||||||
context.commit('setUsers', { users: response.data });
|
.catch((error) => {
|
||||||
EventBus.$emit('usersLoaded', response.data);
|
Vue.$log.warn('Error loading users!', error);
|
||||||
})
|
EventBus.$emit('failedLoadingUsers', error);
|
||||||
.catch((error) => {
|
});
|
||||||
Vue.$log.warn('Error loading users!', error);
|
},
|
||||||
EventBus.$emit('failedLoadingUsers', error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
loadUserGroups(context: any) {
|
loadUserGroups(context: any) {
|
||||||
return fetchUserGroups(context.state.access_token)
|
return fetchUserGroups(context.state.access_token)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Vue.$log.debug(response);
|
Vue.$log.debug(response);
|
||||||
Vue.$log.debug(response.data);
|
Vue.$log.debug(response.data);
|
||||||
context.commit('setUserGroups', { groups: response.data });
|
context.commit('setUserGroups', {groups: response.data});
|
||||||
EventBus.$emit('groupsLoaded', response.data);
|
EventBus.$emit('groupsLoaded', response.data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -74,148 +77,152 @@ const actions = {
|
|||||||
EventBus.$emit('failedLoadingUserGroups', error);
|
EventBus.$emit('failedLoadingUserGroups', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadProfileAuthCheck(context:any){
|
loadProfileAuthCheck(context: any) {
|
||||||
if (!getters.isAuthenticated) {
|
if (!getters.isAuthenticated) {
|
||||||
EventBus.$emit('accessTokenInvalid');
|
EventBus.$emit('accessTokenInvalid');
|
||||||
if (!getters.isRefreshTokenValid) {
|
if (!getters.isRefreshTokenValid) {
|
||||||
Vue.$log.warn('Access and refresh token invalid! User must login again!');
|
Vue.$log.warn('Access and refresh token invalid! User must login again!');
|
||||||
EventBus.$emit('refreshTokenInvalid');
|
EventBus.$emit('refreshTokenInvalid');
|
||||||
EventBus.$emit('accessAndRefreshTokenInvalid');
|
EventBus.$emit('accessAndRefreshTokenInvalid');
|
||||||
} else {
|
} else {
|
||||||
return context.dispatch('refreshToken').then( () => {
|
return context.dispatch('refreshToken').then(() => {
|
||||||
context.commit('loadProfile');
|
context.commit('loadProfile');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.commit('loadProfile');
|
context.commit('loadProfile');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loadProfile(context: any) {
|
loadProfile(context: any) {
|
||||||
return fetchProfile(context.state.access_token)
|
return fetchProfile(context.state.access_token)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Vue.$log.debug(response);
|
Vue.$log.debug(response);
|
||||||
Vue.$log.debug(response.data);
|
Vue.$log.debug(response.data);
|
||||||
context.commit('setProfile', { profile: response.data });
|
context.commit('setProfile', {profile: response.data});
|
||||||
EventBus.$emit('profileLoaded', response.data);
|
EventBus.$emit('profileLoaded', response.data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
Vue.$log.warn('Error loading profile!', error);
|
Vue.$log.warn('Error loading profile!', error);
|
||||||
EventBus.$emit('failedLoadingProfile', error);
|
EventBus.$emit('failedLoadingProfile', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadLoginProviders(context: any) {
|
loadLoginProviders(context: any) {
|
||||||
return getProviders()
|
return getProviders()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
context.commit('setLoginProviderData', {providers: response.data});
|
context.commit('setLoginProviderData', {providers: response.data});
|
||||||
EventBus.$emit('loginProvidersLoaded', response.data);
|
EventBus.$emit('loginProvidersLoaded', response.data);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
login(context: any, userData: any) {
|
login(context: any, userData: any) {
|
||||||
context.commit('setUserData', { userData });
|
context.commit('setUserData', {userData});
|
||||||
return authenticate(userData)
|
return authenticate(userData)
|
||||||
.then((response) => context.commit('setJwtToken', { tokens: response.data }))
|
.then((response) => context.commit('setJwtToken', {tokens: response.data}))
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
Vue.$log.warn('Error Authenticating: ', error);
|
Vue.$log.warn('Error Authenticating: ', error);
|
||||||
EventBus.$emit('failedAuthentication', error);
|
EventBus.$emit('failedAuthentication', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
oidc_login(context: any, redirectionUrl: any) {
|
oidc_login(context: any, redirectionUrl: any) {
|
||||||
// context.commit('setUserData', { userData });
|
// context.commit('setUserData', { userData });
|
||||||
return oidc_login(redirectionUrl)
|
return oidc_login(redirectionUrl)
|
||||||
.then((response) => context.commit('setJwtToken', { tokens: response.data }))
|
.then((response) => context.commit('setJwtToken', {tokens: response.data}))
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
Vue.$log.warn('Error Authenticating: ', error);
|
Vue.$log.warn('Error Authenticating: ', error);
|
||||||
EventBus.$emit('failedAuthentication', error);
|
EventBus.$emit('failedAuthentication', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refreshToken(context: any) {
|
refreshToken(context: any) {
|
||||||
EventBus.$emit('refreshingToken');
|
EventBus.$emit('refreshingToken');
|
||||||
Vue.$log.debug('Refreshing tokens!');
|
Vue.$log.debug('Refreshing tokens!');
|
||||||
return getFreshToken(context.state.refresh_token)
|
return getFreshToken(context.state.refresh_token)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
context.commit('setTokens', { tokens: response.data });
|
context.commit('setTokens', {tokens: response.data});
|
||||||
Vue.$log.debug('Tokens refreshed!'); })
|
Vue.$log.debug('Tokens refreshed!');
|
||||||
.catch((error) => {
|
})
|
||||||
Vue.$log.warn('Error Refreshing token: ', error);
|
.catch((error) => {
|
||||||
EventBus.$emit('failedRefreshingToken', error);
|
Vue.$log.warn('Error Refreshing token: ', error);
|
||||||
});
|
EventBus.$emit('failedRefreshingToken', error);
|
||||||
},
|
});
|
||||||
storeTokens(context: any, tokens: any) {
|
},
|
||||||
context.commit('setTokens', {tokens});
|
storeTokens(context: any, tokens: any) {
|
||||||
EventBus.$emit('storedTokens');
|
context.commit('setTokens', {tokens});
|
||||||
},
|
EventBus.$emit('storedTokens');
|
||||||
register(context: any, userData: any) {
|
},
|
||||||
context.commit('setUserData', { userData });
|
register(context: any, userData: any) {
|
||||||
return register(userData)
|
context.commit('setUserData', {userData});
|
||||||
.then(context.dispatch('login', userData))
|
return register(userData)
|
||||||
.catch((error) => {
|
.then(context.dispatch('login', userData))
|
||||||
Vue.$log.warn('Error Registering: ', error);
|
.catch((error) => {
|
||||||
EventBus.$emit('failedRegistering: ', error);
|
Vue.$log.warn('Error Registering: ', error);
|
||||||
});
|
EventBus.$emit('failedRegistering: ', error);
|
||||||
},
|
});
|
||||||
submitNewSurvey(context: any, survey: any) {
|
},
|
||||||
return postNewSurvey(survey, context.state.access_token);
|
submitNewSurvey(context: any, survey: any) {
|
||||||
},
|
return postNewSurvey(survey, context.state.access_token);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
// isolated data mutations
|
// isolated data mutations
|
||||||
setSurveys(sState: any, payload: any) {
|
setSurveys(sState: any, payload: any) {
|
||||||
sState.surveys = payload.surveys;
|
sState.surveys = payload.surveys;
|
||||||
},
|
},
|
||||||
setSurvey(sState: any, payload: any) {
|
setSurvey(sState: any, payload: any) {
|
||||||
const nQuestions = payload.survey.questions.length;
|
const nQuestions = payload.survey.questions.length;
|
||||||
for (let i = 0; i < nQuestions; i++) {
|
for (let i = 0; i < nQuestions; i++) {
|
||||||
payload.survey.questions[i].choice = null;
|
payload.survey.questions[i].choice = null;
|
||||||
}
|
}
|
||||||
sState.currentSurvey = payload.survey;
|
sState.currentSurvey = payload.survey;
|
||||||
},
|
},
|
||||||
setUsers(sState: any, payload: any) {
|
setRooms(sState: any, payload: any) {
|
||||||
sState.users = payload.users;
|
sState.rooms = payload.rooms;
|
||||||
},
|
},
|
||||||
setUserGroups(sState: any, payload: any) {
|
setUsers(sState: any, payload: any) {
|
||||||
sState.userGroups = payload.groups;
|
sState.users = payload.users;
|
||||||
},
|
},
|
||||||
setChoice(sState: any, payload: any) {
|
setUserGroups(sState: any, payload: any) {
|
||||||
const { questionId, choice } = payload;
|
sState.userGroups = payload.groups;
|
||||||
const nQuestions = sState.currentSurvey.questions.length;
|
},
|
||||||
for (let i = 0; i < nQuestions; i++) {
|
setChoice(sState: any, payload: any) {
|
||||||
if (sState.currentSurvey.questions[i].id === questionId) {
|
const {questionId, choice} = payload;
|
||||||
sState.currentSurvey.questions[i].choice = choice;
|
const nQuestions = sState.currentSurvey.questions.length;
|
||||||
break;
|
for (let i = 0; i < nQuestions; i++) {
|
||||||
}
|
if (sState.currentSurvey.questions[i].id === questionId) {
|
||||||
}
|
sState.currentSurvey.questions[i].choice = choice;
|
||||||
},
|
break;
|
||||||
setLoginProviderData(sState: any, payload: any) {
|
}
|
||||||
Vue.$log.debug('got loginProviders = ', payload);
|
}
|
||||||
sState.loginProviders = payload.providers;
|
},
|
||||||
},
|
setLoginProviderData(sState: any, payload: any) {
|
||||||
// probably old ...
|
Vue.$log.debug('got loginProviders = ', payload);
|
||||||
setUserData(sState: any, payload: any) {
|
sState.loginProviders = payload.providers;
|
||||||
Vue.$log.debug('setUserData payload = ', payload);
|
},
|
||||||
sState.userData = payload.userData;
|
// probably old ...
|
||||||
},
|
setUserData(sState: any, payload: any) {
|
||||||
setProfile(sState: any, payload: any) {
|
Vue.$log.debug('setUserData payload = ', payload);
|
||||||
Vue.$log.debug('setProfile payload = ', payload);
|
sState.userData = payload.userData;
|
||||||
sState.profile = payload.profile;
|
},
|
||||||
},
|
setProfile(sState: any, payload: any) {
|
||||||
setJwtToken(sState: any, payload: any) {
|
Vue.$log.debug('setProfile payload = ', payload);
|
||||||
Vue.$log.debug('setJwtToken payload = ', payload);
|
sState.profile = payload.profile;
|
||||||
localStorage.tokens = payload.tokens;
|
},
|
||||||
sState.access_token = payload.tokens.access_token;
|
setJwtToken(sState: any, payload: any) {
|
||||||
sState.refresh_token = payload.tokens.refresh_token;
|
Vue.$log.debug('setJwtToken payload = ', payload);
|
||||||
},
|
localStorage.tokens = payload.tokens;
|
||||||
setTokens(sState: any, payload: any) {
|
|
||||||
Vue.$log.debug('setTokens payload = ', payload);
|
|
||||||
if(payload.tokens.access_token){
|
|
||||||
sState.access_token = payload.tokens.access_token;
|
sState.access_token = payload.tokens.access_token;
|
||||||
}
|
sState.refresh_token = payload.tokens.refresh_token;
|
||||||
if(payload.tokens.refresh_token){
|
},
|
||||||
sState.refresh_token = payload.tokens.refresh_token;
|
setTokens(sState: any, payload: any) {
|
||||||
}
|
Vue.$log.debug('setTokens payload = ', payload);
|
||||||
Vue.$log.debug('access_token: ' + sState.access_token);
|
if (payload.tokens.access_token) {
|
||||||
Vue.$log.debug('refresh_token: ' + sState.refresh_token);
|
sState.access_token = payload.tokens.access_token;
|
||||||
},
|
}
|
||||||
|
if (payload.tokens.refresh_token) {
|
||||||
|
sState.refresh_token = payload.tokens.refresh_token;
|
||||||
|
}
|
||||||
|
Vue.$log.debug('access_token: ' + sState.access_token);
|
||||||
|
Vue.$log.debug('refresh_token: ' + sState.refresh_token);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
@@ -238,11 +245,11 @@ const getters = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
state,
|
state,
|
||||||
actions,
|
actions,
|
||||||
mutations,
|
mutations,
|
||||||
getters,
|
getters,
|
||||||
plugins: [createPersistedState()],
|
plugins: [createPersistedState()],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<div class="home">
|
||||||
|
<b-alert show>Default Alert</b-alert>
|
||||||
<div>
|
<div>
|
||||||
<button v-for="entry in languages" :key="entry.title" @click="changeLocale(entry.language)">
|
<button v-for="entry in languages" :key="entry.title" @click="changeLocale(entry.language)">
|
||||||
<flag :iso="entry.flag" v-bind:squared="false"/>
|
<flag :iso="entry.flag" v-bind:squared="false"/>
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
var path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
css: {
|
css: {
|
||||||
loaderOptions: {
|
loaderOptions: {
|
||||||
sass: {
|
sass: {
|
||||||
|
|||||||
Reference in New Issue
Block a user