moved to bootstrap-vue, there is a bug in vee-validate and bootstrap-vue >rc15
This commit is contained in:
171
src/App.vue
171
src/App.vue
@@ -1,77 +1,124 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div id="nav">
|
||||
<router-link to="/">Home</router-link> |
|
||||
<router-link to="/about">About</router-link> |
|
||||
<router-link to="/login">Login</router-link> |
|
||||
<router-link to="/profile">Profile</router-link> |
|
||||
<!-- 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 id="app">
|
||||
<sync-loader :loading="isLoading"></sync-loader>
|
||||
<div id="nav">
|
||||
<router-link to="/">Home</router-link>
|
||||
|
|
||||
<router-link to="/about">About</router-link>
|
||||
|
|
||||
<router-link to="/login">Login</router-link>
|
||||
|
|
||||
<router-link to="/profile">Profile</router-link>
|
||||
|
|
||||
<router-link :to="{ name: 'rooms' }">{{ $t('rooms') }}</router-link>
|
||||
|
|
||||
<router-link :to="{ name: 'recorders' }">{{ $t('recorders') }}</router-link>
|
||||
|
|
||||
<router-link :to="{ name: 'commands' }">{{ $t('commands') }}</router-link>
|
||||
|
|
||||
<!-- 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> |
|
||||
<span>({{tokenValidity}})</span> |
|
||||
<span>({{refreshTokenValidity}})</span>
|
||||
|
|
||||
<span>({{tokenValidity}})</span> |
|
||||
<span>({{refreshTokenValidity}})</span>
|
||||
</div>
|
||||
<router-view/>
|
||||
</div>
|
||||
<router-view/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getRemainingJwtValiditySeconds} from "@/utils";
|
||||
import {getRemainingJwtValiditySeconds} from '@/utils';
|
||||
import SyncLoader from 'vue-spinner/src/SyncLoader.vue';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
tokenValidity: -1,
|
||||
refreshTokenValidity: -1,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted () {
|
||||
|
||||
this.$nextTick(() =>{
|
||||
window.setInterval(() => {
|
||||
// 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);
|
||||
});
|
||||
export default {
|
||||
components: {
|
||||
SyncLoader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
tokenValidity: -1,
|
||||
refreshTokenValidity: -1,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// todo ...
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
window.setInterval(() => {
|
||||
// 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>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../node_modules/bootstrap/scss/bootstrap.scss';
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
#nav {
|
||||
padding: 30px;
|
||||
a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
&.router-link-exact-active {
|
||||
color: #42b983;
|
||||
@import '../node_modules/bootstrap/scss/bootstrap.scss';
|
||||
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
#nav {
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
a {
|
||||
font-weight: bold;
|
||||
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>
|
||||
|
||||
@@ -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
|
||||
|
||||
import Repository from "./Repository";
|
||||
// @ts-ignore
|
||||
import Repository from './Repository';
|
||||
|
||||
const resource = "/group";
|
||||
const resource = '/group';
|
||||
|
||||
export default {
|
||||
getGroups() {
|
||||
return Repository.get(`${resource}`);
|
||||
},
|
||||
|
||||
getGroup(groupId) {
|
||||
getGroup(groupId: number) {
|
||||
return Repository.get(`${resource}/${groupId}`);
|
||||
},
|
||||
|
||||
createGroup(groupData) {
|
||||
createGroup(groupData: any) {
|
||||
return Repository.post(`${resource}`, groupData);
|
||||
},
|
||||
};
|
||||
@@ -58,6 +58,6 @@ export function fetchUserGroup(jwt: any, groupId: 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}` } });
|
||||
}
|
||||
|
||||
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 {
|
||||
getUsers() {
|
||||
return Repository.get(`${resource}`);
|
||||
},
|
||||
|
||||
getUser(userId) {
|
||||
getUser(userId: number) {
|
||||
return Repository.get(`${resource}/${userId}`);
|
||||
},
|
||||
|
||||
createUser(userData) {
|
||||
createUser(userData: any) {
|
||||
return Repository.post(`${resource}`, userData);
|
||||
},
|
||||
};
|
||||
@@ -22,29 +22,31 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { EventBus } from '@/utils'
|
||||
import {EventBus} from '@/utils';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
errorMsg: '',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
authenticate () {
|
||||
this.$store.dispatch('login', { email: this.email, password: this.password })
|
||||
authenticate() {
|
||||
this.$store.dispatch('login', {email: this.email, password: this.password})
|
||||
.then(() => this.$router.push('/'));
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$log.debug("Admin: mounting...");
|
||||
mounted() {
|
||||
this.$log.debug('Admin: mounting...');
|
||||
},
|
||||
beforeDestroy () {
|
||||
beforeDestroy() {
|
||||
// todo ...
|
||||
},
|
||||
computed: {
|
||||
// todo ...
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -26,7 +27,8 @@
|
||||
<div class="number"
|
||||
v-for="i in num_group_pages()"
|
||||
v-bind:class="[i == currentPage ? 'active' : '']"
|
||||
v-on:click="change_page(i)">{{i}}</div>
|
||||
v-on:click="change_page(i)">{{i}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,17 +52,18 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { EventBus } from '@/utils'
|
||||
import { RepositoryFactory} from "@/api/RepositoryFactory";
|
||||
const GroupRepository = RepositoryFactory.get('group');
|
||||
import {EventBus} from '@/utils';
|
||||
import getRepository from '@/api/RepositoryFactory';
|
||||
|
||||
const GroupRepository = getRepository('group');
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
groups: [],
|
||||
errorMsg: '',
|
||||
group: {name:'', description:''},
|
||||
group: {name: '', description: ''},
|
||||
sortColumn: '',
|
||||
currentPage: 1,
|
||||
elementsPerPage: 5,
|
||||
@@ -71,7 +74,7 @@
|
||||
this.isLoading = true;
|
||||
// const { data } = await GroupRepository.get();
|
||||
GroupRepository.getGroups()
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.groups = response.data;
|
||||
this.isLoading = false;
|
||||
this.$log.debug(response);
|
||||
@@ -79,8 +82,8 @@
|
||||
|
||||
},
|
||||
get_group_rows() {
|
||||
var start = (this.currentPage-1) * this.elementsPerPage;
|
||||
var end = start + this.elementsPerPage;
|
||||
const start = (this.currentPage - 1) * this.elementsPerPage;
|
||||
const end = start + this.elementsPerPage;
|
||||
return this.groups.slice(start, end);
|
||||
},
|
||||
num_group_pages() {
|
||||
@@ -97,19 +100,19 @@
|
||||
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]) {
|
||||
return ascending ? 1 : -1
|
||||
return ascending ? 1 : -1;
|
||||
} else if (a[col] < b[col]) {
|
||||
return ascending ? -1 : 1
|
||||
return ascending ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
});
|
||||
},
|
||||
createGroup(){
|
||||
this.$log.info("Creating new group...");
|
||||
createGroup() {
|
||||
this.$log.info('Creating new group...');
|
||||
GroupRepository.createGroup(this.group);
|
||||
this.fetch();
|
||||
},
|
||||
@@ -118,6 +121,7 @@
|
||||
this.fetch();
|
||||
},
|
||||
beforeDestroy() {
|
||||
// todo....or not
|
||||
},
|
||||
computed: {
|
||||
group_columns() {
|
||||
@@ -127,7 +131,7 @@
|
||||
return Object.keys(this.groups[0]);
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -135,6 +139,7 @@
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
width: 750px;
|
||||
@@ -152,17 +157,21 @@
|
||||
padding: 8px;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
table th:hover {
|
||||
background: #717699;
|
||||
}
|
||||
|
||||
table td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border-right: 2px solid #7D82A8;
|
||||
}
|
||||
|
||||
table td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
table tbody tr:nth-child(2n) td {
|
||||
background: #D4D8F9;
|
||||
}
|
||||
@@ -184,17 +193,21 @@
|
||||
padding: 8px;
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
table th:hover {
|
||||
background: #717699;
|
||||
}
|
||||
|
||||
table td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border-right: 2px solid #7D82A8;
|
||||
}
|
||||
|
||||
table td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
table tbody tr:nth-child(2n) td {
|
||||
background: #D4D8F9;
|
||||
}
|
||||
@@ -209,9 +222,11 @@
|
||||
.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')
|
||||
}
|
||||
|
||||
.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=')
|
||||
}
|
||||
|
||||
.arrow {
|
||||
float: right;
|
||||
width: 12px;
|
||||
@@ -230,6 +245,7 @@
|
||||
margin: 0px 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.number:hover, .number.active {
|
||||
background: #717699;
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(provider, index) in loginProviders" v-bind:id="index">
|
||||
<a :href="provider.url">{{ index }} ({{provider.type}})</a>
|
||||
</li>
|
||||
<li v-for="(provider, index) in loginProviders" v-bind:id="index">
|
||||
<a :href="provider.url">{{ index }} ({{provider.type}})</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Users</h3>
|
||||
<ul>
|
||||
<li v-for="(user) in users" >
|
||||
<li v-for="(user) in users">
|
||||
<a href="#">({{user.id}}) {{user.first_name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -50,10 +50,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { EventBus } from '@/utils'
|
||||
import {EventBus} from '@/utils';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
password: '',
|
||||
@@ -62,19 +63,20 @@
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
authenticate () {
|
||||
this.$store.dispatch('login', { email: this.email, password: this.password })
|
||||
authenticate() {
|
||||
this.$store.dispatch('login', {email: this.email, password: this.password})
|
||||
.then(() => this.$router.push('/'));
|
||||
},
|
||||
register () {
|
||||
this.$store.dispatch('register', { email: this.email, password: this.password })
|
||||
register() {
|
||||
this.$store.dispatch('register', {email: this.email, password: this.password})
|
||||
.then(() => this.$router.push('/'));
|
||||
},
|
||||
oidc_login () {
|
||||
this.$store.dispatch('oidc_login', "\\oidc_login_redirection");
|
||||
oidc_login() {
|
||||
this.$store.dispatch('oidc_login', '\\oidc_login_redirection');
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
// this.$parent.$data.isLoading = true;
|
||||
EventBus.$on('failedRegistering', (msg) => {
|
||||
this.errorMsg = msg;
|
||||
});
|
||||
@@ -91,22 +93,22 @@
|
||||
this.$store.dispatch('loadLoginProviders');
|
||||
|
||||
// get tokens
|
||||
if(this.$cookies.isKey("tokens")){
|
||||
this.$store.dispatch('storeTokens', JSON.parse(atob(this.$cookies.get("tokens"))));
|
||||
this.$cookies.remove("tokens");
|
||||
if (this.$cookies.isKey('tokens')) {
|
||||
this.$store.dispatch('storeTokens', JSON.parse(atob(this.$cookies.get('tokens'))));
|
||||
this.$cookies.remove('tokens');
|
||||
}
|
||||
console.log(this.$cookies.keys());
|
||||
this.$log.debug(this.$cookies.keys());
|
||||
},
|
||||
beforeDestroy () {
|
||||
beforeDestroy() {
|
||||
EventBus.$off('failedRegistering');
|
||||
EventBus.$off('failedAuthentication');
|
||||
},
|
||||
computed: {
|
||||
users () {
|
||||
users() {
|
||||
return this.$store.state.users;
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
||||
<script>
|
||||
import { EventBus } from '@/utils'
|
||||
import {getRemainingJwtValiditySeconds} from "../utils";
|
||||
import {EventBus} from '@/utils';
|
||||
import {getRemainingJwtValiditySeconds} from '../utils';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
errorMsg: '',
|
||||
@@ -30,19 +30,19 @@
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
authenticate () {
|
||||
this.$store.dispatch('login', { email: this.email, password: this.password })
|
||||
authenticate() {
|
||||
this.$store.dispatch('login', {email: this.email, password: this.password})
|
||||
.then(() => this.$router.push('/'));
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$log.debug("Profile: mounting...");
|
||||
mounted() {
|
||||
this.$log.debug('Profile: mounting...');
|
||||
EventBus.$on('failedLoadingProfile', (msg) => {
|
||||
this.errorMsg = msg;
|
||||
});
|
||||
this.$store.dispatch('loadProfile');
|
||||
|
||||
this.$nextTick(() =>{
|
||||
this.$nextTick( () => {
|
||||
window.setInterval(() => {
|
||||
this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
|
||||
this.tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
|
||||
@@ -52,15 +52,15 @@
|
||||
|
||||
|
||||
},
|
||||
beforeDestroy () {
|
||||
beforeDestroy() {
|
||||
EventBus.$off('failedLoadingProfile');
|
||||
},
|
||||
computed: {
|
||||
profile () {
|
||||
profile() {
|
||||
return this.$store.state.profile;
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<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="container has-text-centered">
|
||||
<h3 class="title">Manage users</h3>
|
||||
<div>{{users}}</div>
|
||||
<div>{{users}}</div>
|
||||
|
||||
<div id="userTable">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -26,7 +27,8 @@
|
||||
<div class="number"
|
||||
v-for="i in num_pages()"
|
||||
v-bind:class="[i === currentPage ? 'active' : '']"
|
||||
v-on:click="change_page(i)">{{i}}</div>
|
||||
v-on:click="change_page(i)">{{i}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,14 +49,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { EventBus } from '@/utils';
|
||||
import {EventBus} from '@/utils';
|
||||
import {getRemainingJwtValiditySeconds} from '../utils';
|
||||
import { RepositoryFactory} from '@/api/RepositoryFactory';
|
||||
const UserRepository = RepositoryFactory.get('user');
|
||||
import getRepository from '@/api/RepositoryFactory';
|
||||
|
||||
const UserRepository = getRepository('user');
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
user: {},
|
||||
users: [],
|
||||
errorMsg: '',
|
||||
@@ -68,7 +72,7 @@
|
||||
this.isLoading = true;
|
||||
// const { data } = await GroupRepository.get();
|
||||
UserRepository.getUsers()
|
||||
.then( (response) => {
|
||||
.then((response) => {
|
||||
this.users = response.data;
|
||||
this.isLoading = false;
|
||||
this.$log.debug(response);
|
||||
@@ -76,8 +80,8 @@
|
||||
|
||||
},
|
||||
get_rows() {
|
||||
let start = (this.currentPage-1) * this.elementsPerPage;
|
||||
let end = start + this.elementsPerPage;
|
||||
const start = (this.currentPage - 1) * this.elementsPerPage;
|
||||
const end = start + this.elementsPerPage;
|
||||
return this.users.slice(start, end);
|
||||
},
|
||||
num_pages() {
|
||||
@@ -94,19 +98,19 @@
|
||||
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]) {
|
||||
return ascending ? 1 : -1
|
||||
return ascending ? 1 : -1;
|
||||
} else if (a[col] < b[col]) {
|
||||
return ascending ? -1 : 1
|
||||
return ascending ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
});
|
||||
},
|
||||
createUser(){
|
||||
this.$log.info("Creating new user...");
|
||||
createUser() {
|
||||
this.$log.info('Creating new user...');
|
||||
UserRepository.createUser(this.user);
|
||||
this.fetch();
|
||||
},
|
||||
@@ -115,6 +119,7 @@
|
||||
this.fetch();
|
||||
},
|
||||
beforeDestroy() {
|
||||
// todo
|
||||
},
|
||||
computed: {
|
||||
columns() {
|
||||
@@ -124,7 +129,7 @@
|
||||
return Object.keys(this.users[0]);
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
20
src/main.ts
20
src/main.ts
@@ -1,6 +1,7 @@
|
||||
import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
import VueAxios from 'vue-axios';
|
||||
import BootstrapVue from 'bootstrap-vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import store from './store';
|
||||
@@ -8,16 +9,29 @@ import VueSweetalert2 from 'vue-sweetalert2';
|
||||
import VueCookies from 'vue-cookies';
|
||||
import VueLogger from 'vuejs-logger';
|
||||
import i18n from '@/plugins/i18n';
|
||||
import VeeValidate from 'vee-validate';
|
||||
// @ts-ignore
|
||||
import FlagIcon from 'vue-flag-icon';
|
||||
// following is to avoid missing type definitions
|
||||
// const FlagIcon = require('vue-flag-icon');
|
||||
|
||||
import 'bootstrap';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
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';
|
||||
|
||||
library.add(faCoffee, faTrash, faPencilAlt);
|
||||
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||
|
||||
const options = {
|
||||
isEnabled: true,
|
||||
// logLevel : isProduction ? 'error' : 'debug',
|
||||
@@ -31,9 +45,11 @@ const options = {
|
||||
|
||||
Vue.use(VueLogger, options);
|
||||
Vue.use(VueAxios, axios);
|
||||
Vue.use(BootstrapVue);
|
||||
Vue.use(FlagIcon);
|
||||
Vue.use(VueCookies);
|
||||
Vue.use(VueSweetalert2);
|
||||
Vue.use(VeeValidate);
|
||||
|
||||
// setup fake backend
|
||||
// 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 NotFound from './views/NotFound.vue';
|
||||
|
||||
import Survey from '@/components/Survey.vue';
|
||||
import NewSurvey from '@/components/NewSurvey.vue';
|
||||
import NewSurvey from '@/components/Rooms.vue';
|
||||
import Login from '@/components/Login.vue';
|
||||
import Admin from '@/components/Admin.vue';
|
||||
import Profile from '@/components/Profile.vue';
|
||||
import User from '@/components/User.vue';
|
||||
import Group from '@/components/Group.vue';
|
||||
import Rooms from '@/components/Rooms.vue';
|
||||
import Recorders from '@/components/Recorders.vue';
|
||||
import store from '@/store';
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
export const router = new Router({
|
||||
// export default new Router({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login,
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'admin',
|
||||
component: Admin,
|
||||
children: [
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
name: 'admin.user',
|
||||
path: 'user',
|
||||
component: User,
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home,
|
||||
},
|
||||
{
|
||||
name: 'admin.group',
|
||||
path: 'group',
|
||||
component: Group,
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// 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: '/surveys/:id',
|
||||
name: 'Survey',
|
||||
component: Survey,
|
||||
}, {
|
||||
path: '/surveys',
|
||||
name: 'NewSurvey',
|
||||
component: NewSurvey,
|
||||
beforeEnter(to, from, next) {
|
||||
if (!store.getters.isAuthenticated) {
|
||||
next('/login');
|
||||
} 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,
|
||||
},
|
||||
],
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'admin',
|
||||
component: Admin,
|
||||
children: [
|
||||
{
|
||||
name: 'admin.user',
|
||||
path: 'user',
|
||||
component: User,
|
||||
},
|
||||
{
|
||||
name: 'admin.group',
|
||||
path: 'group',
|
||||
component: Group,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// 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: '/surveys/:id',
|
||||
name: 'Survey',
|
||||
component: Rooms,
|
||||
}, {
|
||||
path: '/rooms',
|
||||
name: 'rooms',
|
||||
component: Rooms,
|
||||
}, {
|
||||
path: '/recorders',
|
||||
name: 'recorders',
|
||||
component: Recorders,
|
||||
}, {
|
||||
path: '/commands',
|
||||
name: 'commands',
|
||||
component: Rooms,
|
||||
}, {
|
||||
path: '/surveys',
|
||||
name: 'NewSurvey',
|
||||
component: NewSurvey,
|
||||
beforeEnter(to, from, next) {
|
||||
if (!store.getters.isAuthenticated) {
|
||||
next('/login');
|
||||
} 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;
|
||||
|
||||
357
src/store.ts
357
src/store.ts
@@ -1,6 +1,8 @@
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import createPersistedState from 'vuex-persistedstate';
|
||||
import getRepository from '@/api/RepositoryFactory';
|
||||
import RoomRepository from '@/api/roomRepository';
|
||||
|
||||
// imports of AJAX functions will go here
|
||||
import {
|
||||
@@ -14,13 +16,13 @@ import {
|
||||
oidc_login, fetchUsers, getFreshToken, fetchProfile, fetchUserGroups,
|
||||
} from '@/api';
|
||||
import {isValidJwt, EventBus} from '@/utils';
|
||||
import {response} from "express";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const state = {
|
||||
// single source of data
|
||||
surveys: [],
|
||||
rooms: [],
|
||||
loginProviders: [],
|
||||
currentSurvey: {},
|
||||
profile: {},
|
||||
@@ -30,43 +32,44 @@ const state = {
|
||||
refresh_token: '',
|
||||
};
|
||||
|
||||
|
||||
// const RoomRepository = getRepository('room');
|
||||
|
||||
|
||||
const actions = {
|
||||
// asynchronous operations
|
||||
loadSurveys(context: any) {
|
||||
return fetchSurveys()
|
||||
.then((response) => {
|
||||
context.commit('setSurveys', { surveys: response.data });
|
||||
});
|
||||
},
|
||||
// @ts-ignore
|
||||
loadSurvey(context: any, { id }) {
|
||||
return fetchSurvey(id)
|
||||
.then((response) => {
|
||||
context.commit('setSurvey', { survey: response.data });
|
||||
});
|
||||
},
|
||||
addSurveyResponse(context: any) {
|
||||
return saveSurveyResponse(context.state.currentSurvey);
|
||||
},
|
||||
loadUsers(context: any) {
|
||||
return fetchUsers(context.state.access_token)
|
||||
.then((response) => {
|
||||
Vue.$log.debug(response);
|
||||
Vue.$log.debug(response.data);
|
||||
context.commit('setUsers', { users: response.data });
|
||||
EventBus.$emit('usersLoaded', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error loading users!', error);
|
||||
EventBus.$emit('failedLoadingUsers', error);
|
||||
});
|
||||
},
|
||||
// asynchronous operations
|
||||
loadRooms(context: any) {
|
||||
return RoomRepository.getRooms()
|
||||
.then((response: any) => {
|
||||
Vue.$log.debug(response);
|
||||
Vue.$log.debug(response.data);
|
||||
context.commit('setRooms', {rooms: response.data});
|
||||
EventBus.$emit('roomsLoaded', response.data);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
Vue.$log.warn('Error loading users!', error);
|
||||
EventBus.$emit('failedLoadingUsers', error);
|
||||
});
|
||||
},
|
||||
loadUsers(context: any) {
|
||||
return fetchUsers(context.state.access_token)
|
||||
.then((response) => {
|
||||
Vue.$log.debug(response);
|
||||
Vue.$log.debug(response.data);
|
||||
context.commit('setUsers', {users: response.data});
|
||||
EventBus.$emit('usersLoaded', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error loading users!', error);
|
||||
EventBus.$emit('failedLoadingUsers', error);
|
||||
});
|
||||
},
|
||||
loadUserGroups(context: any) {
|
||||
return fetchUserGroups(context.state.access_token)
|
||||
.then((response) => {
|
||||
Vue.$log.debug(response);
|
||||
Vue.$log.debug(response.data);
|
||||
context.commit('setUserGroups', { groups: response.data });
|
||||
context.commit('setUserGroups', {groups: response.data});
|
||||
EventBus.$emit('groupsLoaded', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -74,148 +77,152 @@ const actions = {
|
||||
EventBus.$emit('failedLoadingUserGroups', error);
|
||||
});
|
||||
},
|
||||
loadProfileAuthCheck(context:any){
|
||||
if (!getters.isAuthenticated) {
|
||||
EventBus.$emit('accessTokenInvalid');
|
||||
if (!getters.isRefreshTokenValid) {
|
||||
Vue.$log.warn('Access and refresh token invalid! User must login again!');
|
||||
EventBus.$emit('refreshTokenInvalid');
|
||||
EventBus.$emit('accessAndRefreshTokenInvalid');
|
||||
} else {
|
||||
return context.dispatch('refreshToken').then( () => {
|
||||
context.commit('loadProfile');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
context.commit('loadProfile');
|
||||
}
|
||||
},
|
||||
loadProfile(context: any) {
|
||||
return fetchProfile(context.state.access_token)
|
||||
.then((response) => {
|
||||
Vue.$log.debug(response);
|
||||
Vue.$log.debug(response.data);
|
||||
context.commit('setProfile', { profile: response.data });
|
||||
EventBus.$emit('profileLoaded', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error loading profile!', error);
|
||||
EventBus.$emit('failedLoadingProfile', error);
|
||||
});
|
||||
},
|
||||
loadLoginProviders(context: any) {
|
||||
return getProviders()
|
||||
.then((response) => {
|
||||
context.commit('setLoginProviderData', {providers: response.data});
|
||||
EventBus.$emit('loginProvidersLoaded', response.data);
|
||||
});
|
||||
},
|
||||
login(context: any, userData: any) {
|
||||
context.commit('setUserData', { userData });
|
||||
return authenticate(userData)
|
||||
.then((response) => context.commit('setJwtToken', { tokens: response.data }))
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Authenticating: ', error);
|
||||
EventBus.$emit('failedAuthentication', error);
|
||||
});
|
||||
},
|
||||
oidc_login(context: any, redirectionUrl: any) {
|
||||
// context.commit('setUserData', { userData });
|
||||
return oidc_login(redirectionUrl)
|
||||
.then((response) => context.commit('setJwtToken', { tokens: response.data }))
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Authenticating: ', error);
|
||||
EventBus.$emit('failedAuthentication', error);
|
||||
});
|
||||
},
|
||||
refreshToken(context: any) {
|
||||
EventBus.$emit('refreshingToken');
|
||||
Vue.$log.debug('Refreshing tokens!');
|
||||
return getFreshToken(context.state.refresh_token)
|
||||
.then((response) => {
|
||||
context.commit('setTokens', { tokens: response.data });
|
||||
Vue.$log.debug('Tokens refreshed!'); })
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Refreshing token: ', error);
|
||||
EventBus.$emit('failedRefreshingToken', error);
|
||||
});
|
||||
},
|
||||
storeTokens(context: any, tokens: any) {
|
||||
context.commit('setTokens', {tokens});
|
||||
EventBus.$emit('storedTokens');
|
||||
},
|
||||
register(context: any, userData: any) {
|
||||
context.commit('setUserData', { userData });
|
||||
return register(userData)
|
||||
.then(context.dispatch('login', userData))
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Registering: ', error);
|
||||
EventBus.$emit('failedRegistering: ', error);
|
||||
});
|
||||
},
|
||||
submitNewSurvey(context: any, survey: any) {
|
||||
return postNewSurvey(survey, context.state.access_token);
|
||||
},
|
||||
loadProfileAuthCheck(context: any) {
|
||||
if (!getters.isAuthenticated) {
|
||||
EventBus.$emit('accessTokenInvalid');
|
||||
if (!getters.isRefreshTokenValid) {
|
||||
Vue.$log.warn('Access and refresh token invalid! User must login again!');
|
||||
EventBus.$emit('refreshTokenInvalid');
|
||||
EventBus.$emit('accessAndRefreshTokenInvalid');
|
||||
} else {
|
||||
return context.dispatch('refreshToken').then(() => {
|
||||
context.commit('loadProfile');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
context.commit('loadProfile');
|
||||
}
|
||||
},
|
||||
loadProfile(context: any) {
|
||||
return fetchProfile(context.state.access_token)
|
||||
.then((response) => {
|
||||
Vue.$log.debug(response);
|
||||
Vue.$log.debug(response.data);
|
||||
context.commit('setProfile', {profile: response.data});
|
||||
EventBus.$emit('profileLoaded', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error loading profile!', error);
|
||||
EventBus.$emit('failedLoadingProfile', error);
|
||||
});
|
||||
},
|
||||
loadLoginProviders(context: any) {
|
||||
return getProviders()
|
||||
.then((response) => {
|
||||
context.commit('setLoginProviderData', {providers: response.data});
|
||||
EventBus.$emit('loginProvidersLoaded', response.data);
|
||||
});
|
||||
},
|
||||
login(context: any, userData: any) {
|
||||
context.commit('setUserData', {userData});
|
||||
return authenticate(userData)
|
||||
.then((response) => context.commit('setJwtToken', {tokens: response.data}))
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Authenticating: ', error);
|
||||
EventBus.$emit('failedAuthentication', error);
|
||||
});
|
||||
},
|
||||
oidc_login(context: any, redirectionUrl: any) {
|
||||
// context.commit('setUserData', { userData });
|
||||
return oidc_login(redirectionUrl)
|
||||
.then((response) => context.commit('setJwtToken', {tokens: response.data}))
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Authenticating: ', error);
|
||||
EventBus.$emit('failedAuthentication', error);
|
||||
});
|
||||
},
|
||||
refreshToken(context: any) {
|
||||
EventBus.$emit('refreshingToken');
|
||||
Vue.$log.debug('Refreshing tokens!');
|
||||
return getFreshToken(context.state.refresh_token)
|
||||
.then((response) => {
|
||||
context.commit('setTokens', {tokens: response.data});
|
||||
Vue.$log.debug('Tokens refreshed!');
|
||||
})
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Refreshing token: ', error);
|
||||
EventBus.$emit('failedRefreshingToken', error);
|
||||
});
|
||||
},
|
||||
storeTokens(context: any, tokens: any) {
|
||||
context.commit('setTokens', {tokens});
|
||||
EventBus.$emit('storedTokens');
|
||||
},
|
||||
register(context: any, userData: any) {
|
||||
context.commit('setUserData', {userData});
|
||||
return register(userData)
|
||||
.then(context.dispatch('login', userData))
|
||||
.catch((error) => {
|
||||
Vue.$log.warn('Error Registering: ', error);
|
||||
EventBus.$emit('failedRegistering: ', error);
|
||||
});
|
||||
},
|
||||
submitNewSurvey(context: any, survey: any) {
|
||||
return postNewSurvey(survey, context.state.access_token);
|
||||
},
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
// isolated data mutations
|
||||
setSurveys(sState: any, payload: any) {
|
||||
sState.surveys = payload.surveys;
|
||||
},
|
||||
setSurvey(sState: any, payload: any) {
|
||||
const nQuestions = payload.survey.questions.length;
|
||||
for (let i = 0; i < nQuestions; i++) {
|
||||
payload.survey.questions[i].choice = null;
|
||||
}
|
||||
sState.currentSurvey = payload.survey;
|
||||
},
|
||||
setUsers(sState: any, payload: any) {
|
||||
sState.users = payload.users;
|
||||
},
|
||||
setUserGroups(sState: any, payload: any) {
|
||||
sState.userGroups = payload.groups;
|
||||
},
|
||||
setChoice(sState: any, payload: any) {
|
||||
const { questionId, choice } = payload;
|
||||
const nQuestions = sState.currentSurvey.questions.length;
|
||||
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;
|
||||
},
|
||||
// probably old ...
|
||||
setUserData(sState: any, payload: any) {
|
||||
Vue.$log.debug('setUserData payload = ', payload);
|
||||
sState.userData = payload.userData;
|
||||
},
|
||||
setProfile(sState: any, payload: any) {
|
||||
Vue.$log.debug('setProfile payload = ', payload);
|
||||
sState.profile = payload.profile;
|
||||
},
|
||||
setJwtToken(sState: any, payload: any) {
|
||||
Vue.$log.debug('setJwtToken payload = ', payload);
|
||||
localStorage.tokens = payload.tokens;
|
||||
sState.access_token = payload.tokens.access_token;
|
||||
sState.refresh_token = payload.tokens.refresh_token;
|
||||
},
|
||||
setTokens(sState: any, payload: any) {
|
||||
Vue.$log.debug('setTokens payload = ', payload);
|
||||
if(payload.tokens.access_token){
|
||||
// isolated data mutations
|
||||
setSurveys(sState: any, payload: any) {
|
||||
sState.surveys = payload.surveys;
|
||||
},
|
||||
setSurvey(sState: any, payload: any) {
|
||||
const nQuestions = payload.survey.questions.length;
|
||||
for (let i = 0; i < nQuestions; i++) {
|
||||
payload.survey.questions[i].choice = null;
|
||||
}
|
||||
sState.currentSurvey = payload.survey;
|
||||
},
|
||||
setRooms(sState: any, payload: any) {
|
||||
sState.rooms = payload.rooms;
|
||||
},
|
||||
setUsers(sState: any, payload: any) {
|
||||
sState.users = payload.users;
|
||||
},
|
||||
setUserGroups(sState: any, payload: any) {
|
||||
sState.userGroups = payload.groups;
|
||||
},
|
||||
setChoice(sState: any, payload: any) {
|
||||
const {questionId, choice} = payload;
|
||||
const nQuestions = sState.currentSurvey.questions.length;
|
||||
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;
|
||||
},
|
||||
// probably old ...
|
||||
setUserData(sState: any, payload: any) {
|
||||
Vue.$log.debug('setUserData payload = ', payload);
|
||||
sState.userData = payload.userData;
|
||||
},
|
||||
setProfile(sState: any, payload: any) {
|
||||
Vue.$log.debug('setProfile payload = ', payload);
|
||||
sState.profile = payload.profile;
|
||||
},
|
||||
setJwtToken(sState: any, payload: any) {
|
||||
Vue.$log.debug('setJwtToken payload = ', payload);
|
||||
localStorage.tokens = payload.tokens;
|
||||
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);
|
||||
},
|
||||
sState.refresh_token = payload.tokens.refresh_token;
|
||||
},
|
||||
setTokens(sState: any, payload: any) {
|
||||
Vue.$log.debug('setTokens payload = ', payload);
|
||||
if (payload.tokens.access_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 = {
|
||||
@@ -238,11 +245,11 @@ const getters = {
|
||||
};
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state,
|
||||
actions,
|
||||
mutations,
|
||||
getters,
|
||||
plugins: [createPersistedState()],
|
||||
state,
|
||||
actions,
|
||||
mutations,
|
||||
getters,
|
||||
plugins: [createPersistedState()],
|
||||
});
|
||||
|
||||
export default store;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<b-alert show>Default Alert</b-alert>
|
||||
<div>
|
||||
<button v-for="entry in languages" :key="entry.title" @click="changeLocale(entry.language)">
|
||||
<flag :iso="entry.flag" v-bind:squared="false"/>
|
||||
|
||||
Reference in New Issue
Block a user