Skip to content

Commit

Permalink
enh: Show confirmation dialog before submitting an empty form
Browse files Browse the repository at this point in the history
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
  • Loading branch information
susnux committed Nov 28, 2023
1 parent 3d9ecf4 commit c1248a1
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 20 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ module.exports = {
rules: {
// We are using the @nextcloud/logger
'no-console': ['error', { allow: undefined }],
'import/no-unresolved': ['error', { ignore: ['\\?raw'] }],
},
}
70 changes: 58 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@
"npm": "^9.0.0"
},
"devDependencies": {
"@mdi/svg": "^7.3.67",
"@nextcloud/babel-config": "^1.0.0",
"@nextcloud/browserslist-config": "^3.0.0",
"@nextcloud/eslint-config": "^8.3.0",
"@nextcloud/stylelint-config": "^2.3.1",
"@nextcloud/webpack-vue-config": "^6.0.0"
"@nextcloud/webpack-vue-config": "^6.0.0",
"raw-loader": "^4.0.2"
}
}
65 changes: 58 additions & 7 deletions src/views/Submit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
:name="t('forms', 'Thank you for completing the form!')"
:description="form.submissionMessage">
<template #icon>
<IconCheck :size="64" />
<NcIconSvgWrapper :svg="IconCheckSvg" :size="64" />
</template>
<template v-if="submissionMessageHTML" #description>
<!-- eslint-disable-next-line vue/no-v-html -->
Expand All @@ -82,7 +82,7 @@
:name="t('forms', 'Form expired')"
:description="t('forms', 'This form has expired and is no longer taking answers')">
<template #icon>
<IconCheck :size="64" />
<NcIconSvgWrapper :svg="IconCheckSvg" size="64" />
</template>
</NcEmptyContent>
Expand Down Expand Up @@ -112,6 +112,12 @@
:disabled="loading"
:aria-label="t('forms', 'Submit form')">
</form>
<!-- Confirmation dialog if form is empty submitted -->
<NcDialog :open.sync="showConfirmEmptyModal"
:name="t('forms', 'Confirm submit')"
:message="t('forms', 'Are you sure you want to submit an empty form?')"
:buttons="confirmEmptyModalButtons" />
</NcAppContent>
</template>
Expand All @@ -120,12 +126,17 @@ import { loadState } from '@nextcloud/initial-state'
import { generateOcsUrl } from '@nextcloud/router'
import { showError } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import axios from '@nextcloud/axios'
import moment from '@nextcloud/moment'
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import IconCheck from 'vue-material-design-icons/Check.vue'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import IconCancelSvg from '@mdi/svg/svg/cancel.svg?raw'
import IconCheckSvg from '@mdi/svg/svg/check.svg?raw'
import answerTypes from '../models/AnswerTypes.js'
import logger from '../utils/Logger.js'
Expand All @@ -142,10 +153,11 @@ export default {
name: 'Submit',
components: {
IconCheck,
NcAppContent,
NcDialog,
NcEmptyContent,
NcLoadingIcon,
NcIconSvgWrapper,
Question,
QuestionLong,
QuestionShort,
Expand Down Expand Up @@ -197,15 +209,24 @@ export default {
return {
maxStringLengths: loadState('forms', 'maxStringLengths'),
answerTypes,
/**
* Mapping of questionId => answers
* @type {Record<number, string[]>}
*/
answers: {},
loading: false,
success: false,
/** Submit state of the form, true if changes are currently submitted */
submitForm: false,
showConfirmEmptyModal: false,
}
},
computed: {
IconCheckSvg() {
return IconCheckSvg
},
validQuestions() {
return this.form.questions.filter(question => {
// All questions must have a valid title
Expand Down Expand Up @@ -264,6 +285,22 @@ export default {
}
return t('forms', 'Expires {relativeDate}.', { relativeDate })
},
/**
* Buttons for the "confirm submit empty form" dialog
*/
confirmEmptyModalButtons() {
return [{
label: t('forms', 'Abort'),
icon: IconCancelSvg,
callback: () => {},
}, {
label: t('forms', 'Submit'),
icon: IconCheckSvg,
type: 'primary',
callback: () => this.onConfirmedSubmit(),
}]
},
},
watch: {
Expand Down Expand Up @@ -420,18 +457,32 @@ export default {
},
/**
* Submit the form after the browser validated it 🚀
* Submit the form after the browser validated it 🚀 or show confirmation modal if empty
*/
onSubmit() {
// in case no answer is set or all are empty show the confirmation dialog
if (Object.keys(this.answers).length === 0 || Object.values(this.answers).every((answers) => answers.length === 0)) {
this.showConfirmEmptyModal = true
} else {
// otherwise do the real submit
this.onConfirmedSubmit()
}
},
/**
* Handle the real submit of the form, this is only called if the form is not empty or user confirmed to submit
*/
async onSubmit() {
async onConfirmedSubmit() {
this.showConfirmEmptyModal = false
this.loading = true
this.submitForm = true
try {
await axios.post(generateOcsUrl('apps/forms/api/v2.1/submission/insert'), {
formId: this.form.id,
answers: this.answers,
shareHash: this.shareHash,
})
this.submitForm = true
this.success = true
this.deleteFormFieldFromLocalStorage()
emit('forms:last-updated:set', this.form.id)
Expand Down
12 changes: 12 additions & 0 deletions webpack.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
const path = require('path')
const webpackConfig = require('@nextcloud/webpack-vue-config')
const webpackRules = require('@nextcloud/webpack-vue-config/rules')

webpackConfig.entry.emptyContent = path.resolve(path.join('src', 'emptyContent.js'))
webpackConfig.entry.submit = path.resolve(path.join('src', 'submit.js'))
webpackConfig.entry.settings = path.resolve(path.join('src', 'settings.js'))

delete webpackRules.RULE_ASSETS

webpackConfig.module.rules = [
{
test: /\.svg$/i,
use: 'raw-loader',
resourceQuery: /raw/,
},
...Object.values(webpackRules),
]

module.exports = webpackConfig

0 comments on commit c1248a1

Please sign in to comment.