-
-
-
diff --git a/src/mixins/QuestionMixin.js b/src/mixins/QuestionMixin.js
new file mode 100644
index 0000000..27a73de
--- /dev/null
+++ b/src/mixins/QuestionMixin.js
@@ -0,0 +1,113 @@
+/**
+ * @copyright Copyright (c) 2020 John Molakvoæ
+ *
+ * @author John Molakvoæ
+ *
+ * @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 .
+ */
+import Question from '../components/Questions/Question'
+
+export default {
+ inheritAttrs: false,
+ props: {
+
+ /**
+ * The question title
+ */
+ text: {
+ type: String,
+ required: true,
+ },
+
+ /**
+ * The user answers
+ */
+ values: {
+ type: Array,
+ default() {
+ return []
+ },
+ },
+
+ /**
+ * The question list of answers
+ */
+ options: {
+ type: Array,
+ required: true,
+ },
+
+ /**
+ * Answer type model object
+ */
+ model: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ components: {
+ Question,
+ },
+
+ data() {
+ return {
+ // Do we display this question in edit or fill mode
+ edit: false,
+ }
+ },
+
+ methods: {
+ /**
+ * Forward the title change to the parent
+ *
+ * @param {string} text the title
+ */
+ onTitleChange(text) {
+ this.$emit('update:text', text)
+ },
+
+ /**
+ * Forward the answer(s) change to the parent
+ *
+ * @param {Array} values the array of answers
+ */
+ onValuesChange(values) {
+ this.$emit('update:values', values)
+ },
+
+ /**
+ * Delete this question
+ */
+ onDelete() {
+ this.$emit('delete')
+ },
+
+ /**
+ * Focus the first focusable element
+ */
+ focus() {
+ this.edit = true
+ this.$el.scrollIntoView({ behavior: 'smooth' })
+ this.$nextTick(() => {
+ const title = this.$el.querySelector('.question__header-title')
+ if (title) {
+ title.select()
+ }
+ })
+ },
+ },
+}
diff --git a/src/mixins/ViewsMixin.js b/src/mixins/ViewsMixin.js
index 3a03924..ae79459 100644
--- a/src/mixins/ViewsMixin.js
+++ b/src/mixins/ViewsMixin.js
@@ -19,16 +19,36 @@
* along with this program. If not, see .
*/
+import { generateUrl } from '@nextcloud/router'
+import { showError } from '@nextcloud/dialogs'
+import axios from '@nextcloud/axios'
+
export default {
props: {
hash: {
type: String,
- default: null,
+ default: '',
},
form: {
type: Object,
- // TODO: use default Form object ?
- default: {},
+ required: true,
+ },
+ },
+
+ methods: {
+ async saveFormProperty(key) {
+ try {
+ // TODO: add loading status feedback ?
+ await axios.post(generateUrl('/apps/forms/api/v1/form/update'), {
+ id: this.form.id,
+ keyValuePairs: {
+ [key]: this.form[key],
+ },
+ })
+ } catch (error) {
+ showError(t('forms', 'Error while saving form'))
+ console.error(error)
+ }
},
},
}
diff --git a/src/models/Form.js b/src/models/AnswerTypes.js
similarity index 50%
rename from src/models/Form.js
rename to src/models/AnswerTypes.js
index 943e760..e880b32 100644
--- a/src/models/Form.js
+++ b/src/models/AnswerTypes.js
@@ -20,34 +20,42 @@
*
*/
-export default class {
+import QuestionLong from '../components/Questions/QuestionLong'
+import QuestionShort from '../components/Questions/QuestionShort'
+import QuestionMultiple from '../components/Questions/QuestionMultiple'
- #data
+/**
+ * @typedef {Object} AnswerTypes
+ * @property {string} multiple_unique
+ * @property {string} multiple
+ * @property {string} short
+ * @property {string} long
+ */
+export default {
- /**
- * Construct the form
- * @param {Object} data the form data
- */
- constructor(data) {
- // Id check
- if (!('id' in data && typeof data.id === 'number')) {
- throw new Error('A new form must at least contain a valid id')
- }
+ multiple_unique: {
+ component: QuestionMultiple,
+ icon: 'icon-answer-multiple',
+ label: t('forms', 'Multiple choice'),
+ unique: true,
+ },
- // Hash check
- if (!('hash' in data && typeof data.id === 'string')) {
- throw new Error('A new form must at least contain a valid hash')
- }
+ multiple: {
+ component: QuestionMultiple,
+ icon: 'icon-answer-checkbox',
+ label: t('forms', 'Checkboxes'),
+ },
- this.#data = data
- }
+ short: {
+ component: QuestionShort,
+ icon: 'icon-answer-short',
+ label: t('forms', 'Short answer'),
+ },
- get id() {
- return this.#data.id
- }
-
- get hash() {
- return this.#data.hash
- }
+ long: {
+ component: QuestionLong,
+ icon: 'icon-answer-long',
+ label: t('forms', 'Long text'),
+ },
}
diff --git a/src/router.js b/src/router.js
index de954d1..39b6af0 100644
--- a/src/router.js
+++ b/src/router.js
@@ -56,7 +56,7 @@ export default new Router({
{
path: '/:hash',
name: 'fill',
- props: true,
+ props: { default: true },
},
{
path: '/:hash/edit',
@@ -65,13 +65,13 @@ export default new Router({
sidebar: Sidebar,
},
name: 'edit',
- props: true,
+ props: { default: true },
},
{
path: '/:hash/results',
component: Results,
name: 'results',
- props: true,
+ props: { default: true },
},
{
path: '/:hash/clone',
@@ -80,7 +80,7 @@ export default new Router({
sidebar: Sidebar,
},
name: 'clone',
- props: true,
+ props: { default: true },
},
],
})
diff --git a/src/utils/CancelableRequest.js b/src/utils/CancelableRequest.js
new file mode 100644
index 0000000..e2f79e6
--- /dev/null
+++ b/src/utils/CancelableRequest.js
@@ -0,0 +1,57 @@
+/**
+ * @copyright Copyright (c) 2019 Marco Ambrosini
+ *
+ * @author Marco Ambrosini
+ * @author John Molakvoæ
+ *
+ * @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 .
+ *
+ */
+
+import axios from '@nextcloud/axios'
+
+/**
+ * Creates a cancelable axios 'request object'.
+ *
+ * @param {function} request the axios promise request
+ * @returns {Object}
+ */
+const CancelableRequest = function(request) {
+ /**
+ * Generate an axios cancel token
+ */
+ const CancelToken = axios.CancelToken
+ const source = CancelToken.source()
+
+ /**
+ * Execute the request
+ *
+ * @param {string} url the url to send the request to
+ * @param {Object} [options] optional config for the request
+ */
+ const fetch = async function(url, options) {
+ return request(
+ url,
+ Object.assign({ cancelToken: source.token }, { options })
+ )
+ }
+ return {
+ request: fetch,
+ cancel: source.cancel,
+ }
+}
+
+export default CancelableRequest
diff --git a/src/utils/FormsUtils.js b/src/utils/GenRandomId.js
similarity index 67%
rename from src/utils/FormsUtils.js
rename to src/utils/GenRandomId.js
index 487875b..275735e 100644
--- a/src/utils/FormsUtils.js
+++ b/src/utils/GenRandomId.js
@@ -1,3 +1,4 @@
+
/**
* @copyright Copyright (c) 2020 John Molakvoæ
*
@@ -12,26 +13,19 @@
*
* 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
+ * 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 .
- */
-
-/**
- * Format a form object prior to forms v2.0
*
- * @param {Object} form the form raw object
- * @returns {Object} properly formatted form object
*/
-const formatForm = function(form) {
- // clone form
- const newForm = Object.assign({}, form, form.form)
- // cleanup
- delete newForm.event
- return newForm
+const GenRandomId = (length) => {
+ return Math.random()
+ .toString(36)
+ .replace(/[^a-z]+/g, '')
+ .substr(0, length || 5)
}
-export { formatForm }
+export default GenRandomId
diff --git a/src/views/Create.vue b/src/views/Create.vue
index 42b2e28..8999935 100644
--- a/src/views/Create.vue
+++ b/src/views/Create.vue
@@ -3,6 +3,7 @@
-
- @author René Gieling
- @author Nick Gallo
+ - @author John Molakvoæ
-
- @license GNU AGPL version 3 or any later version
-
@@ -26,284 +27,344 @@
-->
-
-