added admin (group, user) interface components

This commit is contained in:
Tobias Kurze
2019-04-10 09:20:13 +02:00
parent a15e0c2d50
commit c043918fb8
13 changed files with 282 additions and 13 deletions

View File

@@ -4,21 +4,66 @@
<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="/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>
</div> |
<span>({{tokenValidity}})</span> |
<span>({{refreshTokenValidity}})</span>
</div>
<router-view/>
</div>
</template>
<script>
import {getRemainingJwtValiditySeconds} from "@/utils";
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);
});
},
}
</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;
}About
}
#nav {
padding: 30px;
a {

12
src/api/Repository.js Normal file
View File

@@ -0,0 +1,12 @@
// Repository.js
import Vue from 'vue';
import axios from "axios";
const baseDomain = "http://localhost:5443";
const API_URL = `${baseDomain}/api/v1`;
export default axios.create({
API_URL,
headers: { headers: { Authorization: `Bearer ${jwt}` } },
});

View File

@@ -45,6 +45,10 @@ export function fetchUsers(jwt: any) {
return axios.get(`${API_URL}/v1/user`, { headers: { Authorization: `Bearer ${jwt}` } });
}
export function createUser(jwt: any, userData: any) {
return axios.post(`${API_URL}/v1/user`, userData, { headers: { Authorization: `Bearer ${jwt}` } });
}
export function fetchProfile(jwt: any) {
Vue.$log.debug("JWT: "+ jwt);
return axios.get(`${API_URL}/v1/user/profile`, { headers: { Authorization: `Bearer ${jwt}` } });

55
src/components/Admin.vue Normal file
View File

@@ -0,0 +1,55 @@
<!-- components/Admin.vue -->
<template>
<div>
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">Admin Tools</h2>
<p class="subtitle error-msg">{{ errorMsg }}</p>
<router-link :to="{ name: 'admin.group'}">{{ $t('group') }}</router-link>
<br>
<router-link :to="{ name: 'admin.user'}">{{ $t('user') }}</router-link>
</div>
</div>
</section>
<hr/>
<section>
<router-view></router-view>
</section>
</div>
</template>
<script>
import { EventBus } from '@/utils'
export default {
data () {
return {
email: '',
errorMsg: '',
};
},
methods: {
authenticate () {
this.$store.dispatch('login', { email: this.email, password: this.password })
.then(() => this.$router.push('/'));
},
},
mounted () {
this.$log.debug("Admin: mounting...");
},
beforeDestroy () {
},
computed: {
},
}
</script>
<style lang="scss">
.error-msg {
color: red;
font-weight: bold;
}
</style>

59
src/components/Group.vue Normal file
View File

@@ -0,0 +1,59 @@
<!-- components/Group.vue -->
<template>
<div>
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">Groups</h2>
<p class="subtitle error-msg">{{ errorMsg }}</p>
<h3 class="title">Add Group</h3>
<input v-model="group.name" placeholder="Group name">
<p>Name is: {{ group.name }}</p>
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ group.description }}</p>
<br>
<textarea v-model="group.message" placeholder="Description for group"></textarea>
<button v-on:click="create_group">{{$t('create_group')}}</button>
</div>
</div>
</section>
</div>
</template>
<script>
import { EventBus } from '@/utils'
import {getRemainingJwtValiditySeconds} from "../utils";
export default {
data () {
return {
email: '',
errorMsg: '',
group: {},
};
},
methods: {
create_group () {
},
},
mounted () {
},
beforeDestroy () {
},
computed: {
},
}
</script>
<style lang="scss">
.error-msg {
color: red;
font-weight: bold;
}
</style>

47
src/components/User.vue Normal file
View File

@@ -0,0 +1,47 @@
<!-- components/User.vue -->
<template>
<div>
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h3 class="title">Manage users</h3>
<p class="subtitle error-msg">{{ errorMsg }}</p>
</div>
</div>
</section>
</div>
</template>
<script>
import { EventBus } from '@/utils'
import {getRemainingJwtValiditySeconds} from "../utils";
export default {
data () {
return {
users: '',
errorMsg: '',
};
},
methods: {
},
mounted () {
},
beforeDestroy () {
},
computed: {
},
}
</script>
<style lang="scss">
.error-msg {
color: red;
font-weight: bold;
}
</style>

View File

@@ -12,6 +12,9 @@ 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';
const isProduction = process.env.NODE_ENV === 'production';
const options = {

View File

@@ -6,7 +6,10 @@ import NotFound from './views/NotFound.vue';
import Survey from '@/components/Survey.vue';
import NewSurvey from '@/components/NewSurvey.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 store from '@/store';
Vue.use(Router);
@@ -23,9 +26,26 @@ export const router = new Router({
},
{
path: '/login',
name: 'Login',
name: 'login',
component: Login,
},
{
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',
@@ -56,6 +76,7 @@ export const router = new Router({
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'));

7
src/scss/_base.scss Normal file
View File

@@ -0,0 +1,7 @@
/*
* Copyright (c) 2019. Tobias Kurze
*/
.router-link-exact-active {
color: #42b983;
}