Fixed stats graph being filled below line. Added page to show after successful observation submission.
This commit is contained in:
parent
ed3b748469
commit
02ed4b8b08
@ -53,7 +53,8 @@ data class ChartDataset(
|
|||||||
val label: String,
|
val label: String,
|
||||||
val backgroundColor: String,
|
val backgroundColor: String,
|
||||||
val borderColor: String,
|
val borderColor: String,
|
||||||
val data: List<Double>
|
val data: List<Double>,
|
||||||
|
val fill: Boolean = false
|
||||||
){
|
){
|
||||||
constructor(label: String, color: String, data: List<Double>): this(label, color, color, data)
|
constructor(label: String, color: String, data: List<Double>): this(label, color, color, data)
|
||||||
}
|
}
|
@ -10,9 +10,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy
|
import org.springframework.security.config.http.SessionCreationPolicy
|
||||||
import org.springframework.security.core.AuthenticationException
|
import org.springframework.security.core.AuthenticationException
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder
|
import org.springframework.security.crypto.password.PasswordEncoder
|
||||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.servlet.ServletException
|
import javax.servlet.ServletException
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
<b-collapse is-nav id="nav_collapse">
|
<b-collapse is-nav id="nav_collapse">
|
||||||
<b-navbar-nav>
|
<b-navbar-nav>
|
||||||
<b-nav-item to="/">New Observation</b-nav-item>
|
<b-nav-item to="/">New Observation</b-nav-item>
|
||||||
|
<b-nav-item to="/observations">View Observations</b-nav-item>
|
||||||
|
<b-nav-item to="/stats">Graphs</b-nav-item>
|
||||||
</b-navbar-nav>
|
</b-navbar-nav>
|
||||||
<!-- Right aligned nav items -->
|
<!-- Right aligned nav items -->
|
||||||
<b-navbar-nav class="ml-auto">
|
<b-navbar-nav class="ml-auto">
|
||||||
@ -31,12 +33,4 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav a.router-link-exact-active {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -6,40 +6,46 @@ import Stats from "./views/Stats.vue";
|
|||||||
import NewSite from "./views/NewSite.vue";
|
import NewSite from "./views/NewSite.vue";
|
||||||
import NewTutor from "./views/NewTutor.vue";
|
import NewTutor from "./views/NewTutor.vue";
|
||||||
import ViewObservations from "./views/ViewObservations.vue";
|
import ViewObservations from "./views/ViewObservations.vue";
|
||||||
|
import ObservationComplete from "./views/ObservationComplete.vue";
|
||||||
|
|
||||||
Vue.use(Router);
|
Vue.use(Router);
|
||||||
|
|
||||||
export default new Router({
|
export default new Router({
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
name: "home",
|
name: "home",
|
||||||
component: Home
|
component: Home
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/observation",
|
path: "/observation",
|
||||||
name: "observation",
|
name: "observation",
|
||||||
component: Observation
|
component: Observation
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/stats",
|
path: "/stats",
|
||||||
name: "stats",
|
name: "stats",
|
||||||
component: Stats
|
component: Stats
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/newsite",
|
path: "/newsite",
|
||||||
name: "newSite",
|
name: "newSite",
|
||||||
component: NewSite
|
component: NewSite
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/newtutor",
|
path: "/newtutor",
|
||||||
name: "newTutor",
|
name: "newTutor",
|
||||||
component: NewTutor
|
component: NewTutor
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/observations",
|
path: "/observations",
|
||||||
name: "observations",
|
name: "observations",
|
||||||
component: ViewObservations
|
component: ViewObservations
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
path: "/complete",
|
||||||
|
name: "complete",
|
||||||
|
component: ObservationComplete
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
@ -91,14 +91,14 @@
|
|||||||
</b-row>
|
</b-row>
|
||||||
</b-form>
|
</b-form>
|
||||||
<b-modal id="submissionModal"
|
<b-modal id="submissionModal"
|
||||||
ref="modal"
|
ref="submissionModal"
|
||||||
title="Enter password to confirm submission"
|
title="Enter password to confirm submission"
|
||||||
@ok="handleOk"
|
@ok="handleOk"
|
||||||
@shown="clearPassword">
|
@shown="clearPassword">
|
||||||
<form @submit.stop.prevent="handleSubmit">
|
<form @submit.stop.prevent="handleSubmit">
|
||||||
<b-form-input type="password"
|
<b-form-input type="password"
|
||||||
placeholder="Enter password"
|
placeholder="Enter password"
|
||||||
v-model="name"></b-form-input>
|
v-model="submitPassword"></b-form-input>
|
||||||
</form>
|
</form>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</b-container>
|
</b-container>
|
||||||
@ -209,23 +209,26 @@
|
|||||||
showModal() {
|
showModal() {
|
||||||
this.$refs.submissionModal.show();
|
this.$refs.submissionModal.show();
|
||||||
},
|
},
|
||||||
hideModal() {
|
|
||||||
this.$refs.submissionModal.hide();
|
|
||||||
this.submitPassword = null;
|
|
||||||
},
|
|
||||||
clearPassword() {
|
clearPassword() {
|
||||||
this.submitPassword = null
|
this.submitPassword = null
|
||||||
},
|
},
|
||||||
handleOk(evt) {
|
handleOk(evt) {
|
||||||
// Prevent modal from closing
|
// Prevent modal from closing
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
if (this.password) {
|
if (this.submitPassword !== null) {
|
||||||
this.handleSubmit()
|
this.handleSubmit()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleSubmit() {
|
handleSubmit() {
|
||||||
var form = document.getElementById("submission-form");
|
var form = document.getElementById("submission-form");
|
||||||
if (form.checkValidity()) {
|
if (form.checkValidity()) {
|
||||||
|
let axiosConfig = {
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: this.submitPassword
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var self = this;
|
||||||
Vue.axios
|
Vue.axios
|
||||||
.post("/observation", {
|
.post("/observation", {
|
||||||
site: this.site,
|
site: this.site,
|
||||||
@ -234,9 +237,9 @@
|
|||||||
whom: this.whom,
|
whom: this.whom,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
entries: JSON.parse(JSON.stringify(this.observations))
|
entries: JSON.parse(JSON.stringify(this.observations))
|
||||||
})
|
}, axiosConfig)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
this.hideModal();
|
self.$router.push("/complete");
|
||||||
console.log(response);
|
console.log(response);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
|
23
frontend/src/views/ObservationComplete.vue
Normal file
23
frontend/src/views/ObservationComplete.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<b-container>
|
||||||
|
<h2>Submission Complete</h2>
|
||||||
|
<p>{{ type }}/{{ description }} has been submitted successfully.</p>
|
||||||
|
<b-button size="lg" variant="primary" to="/">
|
||||||
|
Start New Observation
|
||||||
|
</b-button>
|
||||||
|
</b-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
export default {
|
||||||
|
name: "ObservationComplete",
|
||||||
|
computed: {
|
||||||
|
...mapState(["description", "type", "whom", "site", "tutors"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -1,164 +1,170 @@
|
|||||||
<template>
|
<template>
|
||||||
<b-container>
|
<b-container>
|
||||||
<b-modal ref="errorModal" class="text-center" centered hide-header hide-footer>
|
<b-modal ref="errorModal" class="text-center" centered hide-header hide-footer>
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3 class="modal-title w-100">{{ errorStatus }}</h3>
|
<h3 class="modal-title w-100">{{ errorStatus }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-block">
|
<div class="d-block">
|
||||||
<br />
|
<br/>
|
||||||
<span style="font-size:20px;" v-html="errorMessage" />
|
<span style="font-size:20px;" v-html="errorMessage"/>
|
||||||
<button class="btn btn-warning" @click="$refs.errorModal.hide()">
|
<button class="btn btn-warning" @click="$refs.errorModal.hide()">
|
||||||
<v-icon name="exclamation-circle" /> Dismiss</button>
|
<v-icon name="exclamation-circle"/>
|
||||||
</div>
|
Dismiss
|
||||||
</b-modal>
|
</button>
|
||||||
<b-row>
|
</div>
|
||||||
<b-col>
|
</b-modal>
|
||||||
<b-form-group label="Site">
|
<b-row>
|
||||||
<b-form-select class="text-center" v-model="siteSelection" :options="siteOptions" />
|
<b-col>
|
||||||
</b-form-group>
|
<b-form-group label="Site">
|
||||||
</b-col>
|
<b-form-select class="text-center" v-model="siteSelection" :options="siteOptions"/>
|
||||||
<b-col>
|
</b-form-group>
|
||||||
<b-form-group label="Tutor">
|
</b-col>
|
||||||
<b-form-select class="text-center" v-model="tutorSelection" :options="tutorOptions" />
|
<b-col>
|
||||||
</b-form-group>
|
<b-form-group label="Tutor">
|
||||||
</b-col>
|
<b-form-select class="text-center" v-model="tutorSelection" :options="tutorOptions"/>
|
||||||
<b-col>
|
</b-form-group>
|
||||||
<b-form-group label="Who">
|
</b-col>
|
||||||
<b-form-input class="text-center" v-model="whom" type="text"></b-form-input>
|
<b-col>
|
||||||
</b-form-group>
|
<b-form-group label="Who">
|
||||||
</b-col>
|
<b-form-input class="text-center" v-model="whom" type="text"></b-form-input>
|
||||||
<b-col>
|
</b-form-group>
|
||||||
<b-form-group label="From">
|
</b-col>
|
||||||
<date-picker v-model="startDate" @dp-change="changeStartDate" value="startDate" :config="dateOptions" />
|
<b-col>
|
||||||
</b-form-group>
|
<b-form-group label="From">
|
||||||
</b-col>
|
<date-picker v-model="startDate" @dp-change="changeStartDate" value="startDate"
|
||||||
<b-col>
|
:config="dateOptions"/>
|
||||||
<b-form-group label="To">
|
</b-form-group>
|
||||||
<date-picker v-model="endDate" @dp-change="changeEndDate" value="endDate" :config="dateOptions" />
|
</b-col>
|
||||||
</b-form-group>
|
<b-col>
|
||||||
</b-col>
|
<b-form-group label="To">
|
||||||
<b-col>
|
<date-picker v-model="endDate" @dp-change="changeEndDate" value="endDate" :config="dateOptions"/>
|
||||||
<b-button v-on:click="getFilteredAverage()">Refresh</b-button>
|
</b-form-group>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
<b-col>
|
||||||
<b-row>
|
<b-button v-on:click="getFilteredAverage()">Refresh</b-button>
|
||||||
<b-col>
|
</b-col>
|
||||||
<stats-view v-if="chartData != null && chartData != {}" v-bind:chartData="chartData" :options="options" />
|
</b-row>
|
||||||
</b-col>
|
<b-row>
|
||||||
</b-row>
|
<b-col>
|
||||||
</b-container>
|
<stats-view v-if="chartData != null && chartData !== {}" v-bind:chartData="chartData"
|
||||||
|
style="position: relative; height:80vh" :options="options"/>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</b-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import StatsView from "../components/StatsView";
|
import StatsView from "../components/StatsView";
|
||||||
var moment = require("moment");
|
|
||||||
export default {
|
var moment = require("moment");
|
||||||
name: "stats",
|
export default {
|
||||||
components: { StatsView },
|
name: "stats",
|
||||||
data: function() {
|
components: {StatsView},
|
||||||
return {
|
data: function () {
|
||||||
chartData: null,
|
return {
|
||||||
options: {
|
chartData: null,
|
||||||
responsive: true,
|
options: {
|
||||||
tooltips: {
|
responsive: true,
|
||||||
mode: "index",
|
maintainAspectRatio: false,
|
||||||
intersect: false
|
tooltips: {
|
||||||
|
mode: "index",
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
mode: "nearest",
|
||||||
|
intersect: true
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: "Date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: "Average Score"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
errorStatus: null,
|
||||||
|
errorMessage: null,
|
||||||
|
startDate: moment().subtract(7, "days"),
|
||||||
|
endDate: moment(),
|
||||||
|
dateOptions: {
|
||||||
|
format: "DD/MM/YYYY",
|
||||||
|
useCurrent: false
|
||||||
|
},
|
||||||
|
siteSelection: null,
|
||||||
|
siteOptions: [],
|
||||||
|
tutorSelection: null,
|
||||||
|
tutorOptions: [],
|
||||||
|
whom: null
|
||||||
|
};
|
||||||
},
|
},
|
||||||
hover: {
|
methods: {
|
||||||
mode: "nearest",
|
getFilteredAverage: function () {
|
||||||
intersect: true
|
Vue.axios
|
||||||
|
.post("/observations/chartdata", {
|
||||||
|
'site': this.siteSelection,
|
||||||
|
'tutor': this.tutorSelection,
|
||||||
|
'startDate': moment(this.startDate).format("YYYY-MM-DD"),
|
||||||
|
'endDate': moment(this.endDate).format("YYYY-MM-DD"),
|
||||||
|
'whom': this.whom
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.chartData = response.data;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.errorStatus = error.response.status;
|
||||||
|
this.errorMessage = error.response.data;
|
||||||
|
this.$refs.errorModal.show();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTutors: function () {
|
||||||
|
if (this.siteSelection != null) {
|
||||||
|
Vue.axios
|
||||||
|
.get("/site/" + this.siteSelection + "/tutors")
|
||||||
|
.then(response => {
|
||||||
|
this.tutorOptions = response.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeStartDate: function (e) {
|
||||||
|
this.startDate = e.date;
|
||||||
|
},
|
||||||
|
changeEndDate: function (e) {
|
||||||
|
this.endDate = e.date;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scales: {
|
watch: {
|
||||||
xAxes: [
|
siteSelection: function () {
|
||||||
{
|
this.tutorOptions = [];
|
||||||
display: true,
|
this.tutorSelection = null;
|
||||||
scaleLabel: {
|
this.getTutors();
|
||||||
display: true,
|
|
||||||
labelString: "Date"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
yAxes: [
|
mounted() {
|
||||||
{
|
this.getFilteredAverage();
|
||||||
display: true,
|
Vue.axios
|
||||||
scaleLabel: {
|
.get("/site")
|
||||||
display: true,
|
.then(response => {
|
||||||
labelString: "Average Score"
|
this.siteOptions = response.data;
|
||||||
}
|
})
|
||||||
}
|
.catch(error => {
|
||||||
]
|
this.errorStatus = error.response.status;
|
||||||
|
this.errorMessage = error.response.data;
|
||||||
|
this.$refs.errorModal.show();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
errorStatus: null,
|
|
||||||
errorMessage: null,
|
|
||||||
startDate: moment().subtract(7, "days"),
|
|
||||||
endDate: moment(),
|
|
||||||
dateOptions: {
|
|
||||||
format: "DD/MM/YYYY",
|
|
||||||
useCurrent: false
|
|
||||||
},
|
|
||||||
siteSelection: null,
|
|
||||||
siteOptions: [],
|
|
||||||
tutorSelection: null,
|
|
||||||
tutorOptions: null,
|
|
||||||
whom: null
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getFilteredAverage: function() {
|
|
||||||
Vue.axios
|
|
||||||
.post("/observations/chartdata", {
|
|
||||||
'site': this.siteSelection,
|
|
||||||
'tutor': this.tutorSelection,
|
|
||||||
'startDate': moment(this.startDate).format("YYYY-MM-DD"),
|
|
||||||
'endDate': moment(this.endDate).format("YYYY-MM-DD"),
|
|
||||||
'whom': this.whom
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
this.chartData = response.data;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.errorStatus = error.response.status;
|
|
||||||
this.errorMessage = error.response.data;
|
|
||||||
this.$refs.errorModal.show();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTutors: function() {
|
|
||||||
if (this.siteSelection != null) {
|
|
||||||
Vue.axios
|
|
||||||
.get("/site/" + this.siteSelection + "/tutors")
|
|
||||||
.then(response => {
|
|
||||||
this.tutorOptions = response.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeStartDate: function(e) {
|
|
||||||
this.startDate = e.date;
|
|
||||||
},
|
|
||||||
changeEndDate: function(e) {
|
|
||||||
this.endDate = e.date;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
siteSelection: function() {
|
|
||||||
this.tutorOptions = [];
|
|
||||||
this.tutorSelection = null;
|
|
||||||
this.getTutors();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getFilteredAverage();
|
|
||||||
Vue.axios
|
|
||||||
.get("/site")
|
|
||||||
.then(response => {
|
|
||||||
this.siteOptions = response.data;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.errorStatus = error.response.status;
|
|
||||||
this.errorMessage = error.response.data;
|
|
||||||
this.$refs.errorModal.show();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user