Version 1.0.3
This commit is contained in:
parent
da21b60ef0
commit
1fce2a39fb
30 changed files with 174 additions and 1006 deletions
|
|
@ -23,6 +23,9 @@
|
|||
<template>
|
||||
<div class="controls">
|
||||
<div class="breadcrumb">
|
||||
<button class="button btn primary" @click="helpPage">
|
||||
{{ "Help" }}
|
||||
</button>
|
||||
<div class="crumb svg crumbhome">
|
||||
<router-link :to="{ name: 'list'}" class="icon-home">
|
||||
Home
|
||||
|
|
@ -52,6 +55,11 @@ export default {
|
|||
return {
|
||||
imagePath: OC.imagePath('core', 'places/home.svg')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
helpPage() {
|
||||
window.open('https://github.com/affan98/forms/blob/master/Forms_Support.md')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
<div>
|
||||
<img class="icontwo">
|
||||
</div>
|
||||
<div v-if="votedBycurrentUser" class="symbol icon-voted" />
|
||||
<div class="symbol icon-voted" />
|
||||
<a :href="voteUrl" class="wrapper group-1-1">
|
||||
<div class="name">
|
||||
{{ form.event.title }}
|
||||
|
|
@ -173,19 +173,11 @@ export default {
|
|||
return t('forms', 'never')
|
||||
}
|
||||
},
|
||||
participants() {
|
||||
return this.form.votes.map(item => item.userId)
|
||||
.filter((value, index, self) => self.indexOf(value) === index)
|
||||
},
|
||||
countvotes() {
|
||||
return this.participants.length
|
||||
},
|
||||
|
||||
countShares() {
|
||||
return this.form.shares.length
|
||||
},
|
||||
votedBycurrentUser() {
|
||||
return this.participants.indexOf(OC.getCurrentUser().uid) > -1
|
||||
},
|
||||
|
||||
voteUrl() {
|
||||
return OC.generateUrl('apps/forms/form/') + this.form.event.hash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<li>
|
||||
<div>{{ question.text }}</div>
|
||||
<div>
|
||||
<input v-show="question.type != 'text'" style="height:30px;" v-model="newQuizAnswer"
|
||||
<input v-show="(question.type != 'text') && (question.type != 'comment')" style="height:30px;" v-model="newQuizAnswer"
|
||||
:placeholder=" t('forms', 'Add Answer')"
|
||||
@keyup.enter="emitNewAnswer(question)"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import Router from 'vue-router'
|
|||
// Dynamic loading
|
||||
const Create = () => import('./views/Create')
|
||||
const List = () => import('./views/List')
|
||||
const Vote = () => import('./views/Vote')
|
||||
const Results = () => import('./views/Results')
|
||||
Vue.use(Router)
|
||||
|
||||
|
|
@ -76,14 +75,6 @@ export default new Router({
|
|||
},
|
||||
props: false,
|
||||
name: 'create'
|
||||
},
|
||||
{
|
||||
path: '/:index(index.php/)?apps/forms/form/:hash',
|
||||
components: {
|
||||
default: Vote
|
||||
},
|
||||
props: false,
|
||||
name: 'create'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -129,10 +129,6 @@
|
|||
<input v-show="form.event.isAnonymous" id="trueAnonymous" v-model="form.event.fullAnonymous"
|
||||
:disabled="protect" type="checkbox" class="checkbox"
|
||||
>
|
||||
<label v-show="form.event.isAnonymous" class="title" for="trueAnonymous">
|
||||
{{ t('forms', 'Hide user names for admin') }}
|
||||
</label>
|
||||
|
||||
<input id="expiration" v-model="form.event.expiration" :disabled="protect"
|
||||
type="checkbox" class="checkbox"
|
||||
>
|
||||
|
|
@ -255,7 +251,8 @@ export default {
|
|||
options: [
|
||||
{ text: 'Radio Buttons', value: 'radiogroup' },
|
||||
{ text: 'Checkboxes', value: 'checkbox' },
|
||||
{ text: 'Write In', value: 'text' },
|
||||
{ text: 'Short Response', value: 'text' },
|
||||
{ text: 'Long Response', value: 'comment' },
|
||||
{ text: 'Drop Down', value: 'dropdown' }
|
||||
]
|
||||
}
|
||||
|
|
@ -285,7 +282,7 @@ export default {
|
|||
} else if (this.form.mode === 'edit') {
|
||||
return t('forms', 'Update form')
|
||||
} else {
|
||||
return t('forms', 'Create new form')
|
||||
return t('forms', 'Done')
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -412,7 +409,7 @@ export default {
|
|||
} else if (!this.uniqueName) {
|
||||
OC.Notification.showTemporary(t('forms', 'Cannot have the same question!'))
|
||||
} else {
|
||||
if (this.newQuizQuestion !== null & this.newQuizQuestion !== '') {
|
||||
if (this.newQuizQuestion !== null & this.newQuizQuestion !== '' & (/\S/.test(this.newQuizQuestion))) {
|
||||
this.form.options.formQuizQuestions.push({
|
||||
id: this.nextQuizQuestionId++,
|
||||
text: this.newQuizQuestion,
|
||||
|
|
@ -443,7 +440,7 @@ export default {
|
|||
if (!this.uniqueAnsName) {
|
||||
OC.Notification.showTemporary(t('forms', 'Two answers cannot be the same!'))
|
||||
} else {
|
||||
if (item.newQuizAnswer !== null & item.newQuizAnswer !== '') {
|
||||
if (item.newQuizAnswer !== null & item.newQuizAnswer !== '' & (/\S/.test(item.newQuizAnswer))) {
|
||||
item.formQuizAnswers.push({
|
||||
id: item.nextQuizAnswerId,
|
||||
text: item.newQuizAnswer
|
||||
|
|
@ -461,7 +458,7 @@ export default {
|
|||
allHaveAns() {
|
||||
this.haveAns = true
|
||||
this.form.options.formQuizQuestions.forEach(q => {
|
||||
if (q.type !== 'text' && q.answers.length === 0) {
|
||||
if (q.type !== 'text' && q.type !== 'comment' && q.answers.length === 0) {
|
||||
this.haveAns = false
|
||||
}
|
||||
})
|
||||
|
|
@ -472,13 +469,15 @@ export default {
|
|||
if (mode !== '') {
|
||||
this.form.mode = mode
|
||||
}
|
||||
if (this.form.event.title.length === 0) {
|
||||
if (this.form.event.title.length === 0 | !(/\S/.test(this.form.event.title))) {
|
||||
this.titleEmpty = true
|
||||
OC.Notification.showTemporary(t('forms', 'Title must not be empty!'))
|
||||
} else if (this.form.options.formQuizQuestions.length === 0) {
|
||||
OC.Notification.showTemporary(t('forms', 'Must have at least one question!'))
|
||||
} else if (!this.haveAns) {
|
||||
OC.Notification.showTemporary(t('forms', 'All questions need answers!'))
|
||||
} else if (this.form.event.expiration & this.form.event.expirationDate === '') {
|
||||
OC.Notification.showTemporary(t('forms', 'Need to pick an expiration date!'))
|
||||
} else {
|
||||
this.writingForm = true
|
||||
this.titleEmpty = false
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
</span>
|
||||
</router-link>
|
||||
</controls>
|
||||
|
||||
<div v-if="noForms" class="">
|
||||
<div class="icon-forms" />
|
||||
<h2> {{ t('No existing forms.') }} </h2>
|
||||
|
|
@ -39,7 +38,6 @@
|
|||
<span>{{ t('forms', 'Click here to add a form') }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<transition-group
|
||||
v-if="!noForms"
|
||||
name="list"
|
||||
|
|
@ -102,7 +100,9 @@ export default {
|
|||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
helpPage() {
|
||||
window.open('https://github.com/affan98/forms/blob/master/Forms_Support.md')
|
||||
},
|
||||
viewFormResults(index, event, name) {
|
||||
this.$router.push({
|
||||
name: name,
|
||||
|
|
|
|||
|
|
@ -23,31 +23,39 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div id="app-content">
|
||||
<div>
|
||||
<div>
|
||||
<button class="button btn primary" @click="download">
|
||||
<span>{{ "Export to CSV" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<transition-group
|
||||
name="list"
|
||||
tag="div"
|
||||
class="table"
|
||||
>
|
||||
<resultItem
|
||||
key="0"
|
||||
:header="true"
|
||||
/>
|
||||
<li
|
||||
is="resultItem"
|
||||
v-for="(vote, index) in votes"
|
||||
:key="vote.id"
|
||||
:vote="vote"
|
||||
@viewResults="viewFormResults(index, form.event, 'results')"
|
||||
/>
|
||||
</transition-group>
|
||||
<loading-overlay v-if="loading" />
|
||||
<modal-dialog />
|
||||
<h1>
|
||||
{{ "Statistics" }}
|
||||
</h1>
|
||||
<div v-for="sum in stats" :key="sum">
|
||||
{{ sum }}
|
||||
</div>
|
||||
<div id="app-content">
|
||||
<transition-group
|
||||
name="list"
|
||||
tag="div"
|
||||
class="table"
|
||||
>
|
||||
<resultItem
|
||||
key="0"
|
||||
:header="true"
|
||||
/>
|
||||
<li
|
||||
is="resultItem"
|
||||
v-for="(vote, index) in votes"
|
||||
:key="vote.id"
|
||||
:vote="vote"
|
||||
@viewResults="viewFormResults(index, form.event, 'results')"
|
||||
/>
|
||||
</transition-group>
|
||||
<loading-overlay v-if="loading" />
|
||||
<modal-dialog />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -68,6 +76,7 @@ export default {
|
|||
return {
|
||||
loading: true,
|
||||
votes: []
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -81,7 +90,12 @@ export default {
|
|||
this.loading = true
|
||||
this.$http.get(OC.generateUrl('apps/forms/get/votes/' + this.$route.params.hash))
|
||||
.then((response) => {
|
||||
this.votes = response.data
|
||||
if (response.data == null) {
|
||||
this.votes = null
|
||||
OC.Notification.showTemporary('Access Denied')
|
||||
} else {
|
||||
this.votes = response.data
|
||||
}
|
||||
this.loading = false
|
||||
}, (error) => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
|
|
@ -98,16 +112,57 @@ export default {
|
|||
})
|
||||
},
|
||||
download() {
|
||||
this.json2csvParser = ['userId', 'voteOptionId', 'voteOptionText', 'voteAnswer']
|
||||
var element = document.createElement('a')
|
||||
element.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(json2csvParser.parse(this.votes)))
|
||||
element.setAttribute('download', 'NextCloud Forms CSV' + '.csv')
|
||||
|
||||
element.style.display = 'none'
|
||||
document.body.appendChild(element)
|
||||
this.loading = true
|
||||
this.$http.get(OC.generateUrl('apps/forms/get/event/' + this.$route.params.hash))
|
||||
.then((response) => {
|
||||
this.json2csvParser = ['userId', 'voteOptionId', 'voteOptionText', 'voteAnswer']
|
||||
var element = document.createElement('a')
|
||||
element.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(json2csvParser.parse(this.votes)))
|
||||
element.setAttribute('download', response.data.title + '.csv')
|
||||
|
||||
element.click()
|
||||
document.body.removeChild(element)
|
||||
element.style.display = 'none'
|
||||
document.body.appendChild(element)
|
||||
element.click()
|
||||
document.body.removeChild(element)
|
||||
this.loading = false
|
||||
}, (error) => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(error.response)
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
stats() {
|
||||
|
||||
if (this.votes != null) {
|
||||
var uniqueAns = []
|
||||
var uniqueQs = []
|
||||
var ansToQ = new Map()
|
||||
for (let i = 0; i < this.votes.length; i++) {
|
||||
if (this.votes[i].voteOptionType === 'radiogroup' || this.votes[i].voteOptionType === 'dropdown') {
|
||||
if (uniqueAns.includes(this.votes[i].voteAnswer) === false) {
|
||||
uniqueAns.push(this.votes[i].voteAnswer)
|
||||
ansToQ.set(this.votes[i].voteAnswer, this.votes[i].voteOptionId)
|
||||
}
|
||||
if (uniqueQs.includes(this.votes[i].voteOptionId) === false) {
|
||||
uniqueQs.push(this.votes[i].voteOptionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
var sums = []
|
||||
for (let i = 0; i < uniqueAns.length; i++) {
|
||||
sums[i] = 0
|
||||
}
|
||||
for (let i = 0; i < this.votes.length; i++) {
|
||||
sums[uniqueAns.indexOf(this.votes[i].voteAnswer)]++
|
||||
}
|
||||
for (let i = 0; i < sums.length; i++) {
|
||||
sums[i] = 'Question ' + ansToQ.get(uniqueAns[i]) + ': ' + (sums[i] / ((this.votes.length / uniqueQs.length)) * 100).toFixed(2) + '%' + ' of respondents voted for answer choice: ' + uniqueAns[i]
|
||||
}
|
||||
}
|
||||
return sums.sort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,176 +0,0 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author Natalie Gilbert
|
||||
- @author Nick Gallo
|
||||
- @author Affan Hussain
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
<template>
|
||||
<div id="app-votes">
|
||||
<survey :survey="survey"></survey>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as SurveyVue from 'survey-vue'
|
||||
import * as widgets from 'surveyjs-widgets'
|
||||
// import 'bootstrap/dist/css/bootstrap.css'
|
||||
var Survey = SurveyVue.Survey
|
||||
Survey.cssType = 'bootstrap'
|
||||
|
||||
// import "inputmask/dist/inputmask/phone-codes/phone.js"
|
||||
|
||||
widgets.icheck(SurveyVue)
|
||||
widgets.select2(SurveyVue)
|
||||
widgets.inputmask(SurveyVue)
|
||||
widgets.jquerybarrating(SurveyVue)
|
||||
widgets.jqueryuidatepicker(SurveyVue)
|
||||
widgets.nouislider(SurveyVue)
|
||||
widgets.select2tagbox(SurveyVue)
|
||||
widgets.signaturepad(SurveyVue)
|
||||
widgets.sortablejs(SurveyVue)
|
||||
widgets.ckeditor(SurveyVue)
|
||||
widgets.autocomplete(SurveyVue)
|
||||
widgets.bootstrapslider(SurveyVue)
|
||||
|
||||
export default {
|
||||
name: 'AppVote',
|
||||
components: {
|
||||
Survey
|
||||
},
|
||||
data() {
|
||||
var json = {
|
||||
title: '',
|
||||
questions: []
|
||||
}
|
||||
var model = new SurveyVue.Model(json)
|
||||
return {
|
||||
loadingForm: false,
|
||||
writingForm: false,
|
||||
form: [],
|
||||
myTitle: '',
|
||||
quests: [],
|
||||
survey: model,
|
||||
ans: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.indexPage = OC.generateUrl('apps/forms/')
|
||||
this.loadForm(this.$route.params.hash)
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadForm(hash) {
|
||||
this.loadingForm = true
|
||||
this.$http.get(OC.generateUrl('apps/forms/get/form/' + hash))
|
||||
.then((response) => {
|
||||
this.form = response.data
|
||||
this.myTitle = response.data.event.title
|
||||
this.quests = response.data.options.formQuizQuestions
|
||||
this.loadingForm = false
|
||||
this.setSurvey()
|
||||
}, (error) => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(error.response)
|
||||
this.form.event.hash = ''
|
||||
this.loadingForm = false
|
||||
})
|
||||
},
|
||||
setSurvey() {
|
||||
this.quests.forEach(q => {
|
||||
q.answers.forEach(a => {
|
||||
this.ans.push(a.text)
|
||||
})
|
||||
this.survey.pages[0].addNewQuestion(q.type, q.text).choices = this.ans
|
||||
this.ans = []
|
||||
this.survey.pages[0].questions.forEach(i => {
|
||||
i.isRequired = true
|
||||
})
|
||||
})
|
||||
this.survey
|
||||
.onUpdateQuestionCssClasses
|
||||
.add(function(survey, options) {
|
||||
var classes = options.cssClasses
|
||||
classes.root = 'sq-root'
|
||||
classes.title = 'sq-title'
|
||||
classes.item = 'sq-item'
|
||||
classes.label = 'sq-label'
|
||||
|
||||
if (options.question.isRequired) {
|
||||
classes.title = 'sq-title sq-title-required'
|
||||
classes.root = 'sq-root sq-root-required'
|
||||
}
|
||||
})
|
||||
this.survey
|
||||
.onComplete
|
||||
.add(function(result) {
|
||||
this.writingForm = true
|
||||
this.form.answers = result.data
|
||||
this.form.userId = OC.getCurrentUser().uid
|
||||
this.form.questions = this.quests
|
||||
this.$http.post(OC.generateUrl('apps/forms/insert/vote'), this.form)
|
||||
.then((response) => {
|
||||
this.writingForm = false
|
||||
}, (error) => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(error.response)
|
||||
this.writingForm = false
|
||||
})
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.app-forms {
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.sv_qstn .sq-root {
|
||||
border: 1px solid gray;
|
||||
border-left: 4px solid #18a689;
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sq-title {
|
||||
font-size: 22px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.sq-title-required {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.sq-label {
|
||||
margin-left: 30px;
|
||||
}
|
||||
.sq-item:nth-child(1) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue