Deletion compatibility

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ (skjnldsv) 2020-03-25 17:40:59 +01:00
parent 85d6a1be47
commit ef2e32a571
No known key found for this signature in database
GPG key ID: 60C25B8C072916CF
9 changed files with 133 additions and 43 deletions

View file

@ -33,7 +33,6 @@ return [
['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'],
['name' => 'page#delete_form', 'url' => '/delete', 'verb' => 'POST'],
['name' => 'page#insert_vote', 'url' => '/insert/vote', 'verb' => 'POST'],
['name' => 'page#search', 'url' => '/search', 'verb' => 'POST'],
['name' => 'page#get_display_name', 'url' => '/get/displayname', 'verb' => 'POST'],
@ -44,10 +43,10 @@ return [
['name' => 'api#get_votes', 'url' => '/get/votes/{formId}', 'verb' => 'GET'],
['name' => 'api#get_shares', 'url' => '/get/shares/{formId}', 'verb' => 'GET'],
['name' => 'api#get_event', 'url' => '/get/event/{formId}', 'verb' => 'GET'],
['name' => 'api#remove_form', 'url' => '/forms/{id}', 'verb' => 'DELETE'],
['name' => 'api#get_forms', 'url' => '/get/forms', 'verb' => 'GET'],
['name' => 'api#newForm', 'url' => 'api/v1/form', 'verb' => 'POST'],
['name' => 'api#deleteForm', 'url' => 'api/v1/form/{id}', 'verb' => 'DELETE'],
['name' => 'system#get_site_users_and_groups', 'url' => '/get/siteusers', 'verb' => 'POST'],
]

View file

@ -389,8 +389,9 @@ class ApiController extends Controller {
* @NoAdminRequired
* @param int $formId
* @return DataResponse
* TODO: use hash instead of id ?
*/
public function removeForm(int $id) {
public function deleteForm(int $id) {
try {
$formToDelete = $this->eventMapper->find($id);
} catch (DoesNotExistException $e) {
@ -533,11 +534,6 @@ class ApiController extends Controller {
$this->eventMapper->insert($event);
return new Http\JSONResponse([
'id' => $event->getId(),
'hash' => $event->getHash(),
]);
return new Http\JSONResponse($this->getForm($event->getHash()));
}
}

View file

@ -25,7 +25,10 @@
<Content app-name="forms">
<AppNavigation>
<AppNavigationNew button-class="icon-add" :text="t('forms', 'New form')" @click="onNewForm" />
<AppNavigationForm v-for="form in formattedForms" :key="form.id" :form="form" />
<AppNavigationForm v-for="form in formattedForms"
:key="form.id"
:form="form"
@delete="onDeleteForm" />
</AppNavigation>
<!-- No forms & loading emptycontents -->
@ -57,6 +60,7 @@
<script>
import { showError } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
@ -118,7 +122,7 @@ export default {
async loadForms() {
this.loading = true
try {
const response = await axios.get(OC.generateUrl('apps/forms/get/forms'))
const response = await axios.get(generateUrl('apps/forms/get/forms'))
this.forms = response.data
} catch (error) {
showError(t('forms', 'An error occured while loading the forms list'))
@ -131,8 +135,27 @@ export default {
/**
*
*/
onNewForm() {
this.$router.push({ name: 'create' })
async onNewForm() {
try {
// Request a new empty form
const response = await axios.post(generateUrl('/apps/forms/api/v1/form'))
const newForm = response.data
this.forms.push(newForm)
this.$router.push({ name: 'edit', params: { hash: newForm.event.hash } })
} catch (error) {
showError(t('forms', 'Unable to create a new form'))
console.error(error)
}
},
/**
* Remove form from forms list after successful server deletion request
*
* @param {Number} id the form id
*/
async onDeleteForm(id) {
const formIndex = this.forms.findIndex(form => form.id === id)
this.forms.splice(formIndex, 1)
},
},
}

View file

@ -23,24 +23,25 @@
<template>
<AppNavigationItem
:exact="true"
:icon="loading ? 'icon-loading-small' : ''"
:title="form.title"
:to="{ name: 'edit', params: { hash: form.hash } }">
<AppNavigationIconBullet slot="icon" :color="bulletColor" />
<template #actions>
<template v-if="!loading" #actions>
<ActionRouter :close-after-click="true"
:exact="true"
icon="icon-checkmark"
:to="{ name: 'results', params: { hash: form.hash } }">
{{ t('forms', 'Show results') }}
</ActionRouter>
<ActionRouter :close-after-click="true"
<!-- <ActionRouter :close-after-click="true"
:exact="true"
icon="icon-clone"
:to="{ name: 'clone', params: { hash: form.hash } }">
{{ t('forms', 'Clone form') }}
</ActionRouter>
</ActionRouter> -->
<ActionSeparator />
<ActionButton :close-after-click="true" icon="icon-delete" @click="deleteForm">
<ActionButton :close-after-click="true" icon="icon-delete" @click="onDeleteForm">
{{ t('forms', 'Delete form') }}
</ActionButton>
</template>
@ -48,6 +49,9 @@
</template>
<script>
import { showError, showSuccess } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem'
import AppNavigationIconBullet from '@nextcloud/vue/dist/Components/AppNavigationIconBullet'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
@ -72,6 +76,12 @@ export default {
},
},
data() {
return {
loading: false,
}
},
computed: {
/**
* Map form state to bullet color
@ -88,9 +98,25 @@ export default {
},
methods: {
async deleteForm() {
async onDeleteForm() {
if (!confirm(t('forms', 'Are you sure you want to delete the form “{title}”', { title: this.form.title }))) {
return
}
// All good, let's delete
this.loading = true
try {
await axios.delete(generateUrl('/apps/forms/api/v1/form/{id}', { id: this.form.id }))
this.$emit('delete', this.form.id)
showSuccess(t('forms', 'Deleted form “{title}”', { title: this.form.title }))
} catch (error) {
showError(t('forms', 'Error while deleting form “{title}”', { title: this.form.title }))
console.error(error.response)
} finally {
this.loading = false
}
},
},
}

View file

@ -2,6 +2,7 @@
* @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
*
* @author René Gieling <github@dartcafe.de>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*

53
src/models/Form.js Normal file
View file

@ -0,0 +1,53 @@
/**
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
export default class {
#data
/**
* 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')
}
// Hash check
if (!('hash' in data && typeof data.id === 'string')) {
throw new Error('A new form must at least contain a valid hash')
}
this.#data = data
}
get id() {
return this.#data.id
}
get hash() {
return this.#data.hash
}
}

View file

@ -31,7 +31,7 @@ const formatForm = function(form) {
// migrate object architecture
Object.assign(newForm, {
questions: form.options.formQuizQuestions,
questions: form.options && form.options.formQuizQuestions,
})
// cleanup

View file

@ -269,8 +269,6 @@ export default {
if (this.form.event.title.length === 0 | !(/\S/.test(this.form.event.title))) {
this.titleEmpty = true
showError(t('forms', 'Title must not be empty!'), { duration: 3000 })
} else if (this.form.options.formQuizQuestions.length === 0) {
showError(t('forms', 'Must have at least one question!'), { duration: 3000 })
} else if (!this.haveAns) {
showError(t('forms', 'All questions need answers!'), { duration: 3000 })
} else if (this.form.event.expiration & this.form.event.expirationDate === '') {
@ -323,7 +321,6 @@ input[type="text"] {
}
.workbench {
margin-top: 45px;
display: flex;
flex-grow: 1;
flex-wrap: wrap;

View file

@ -21,7 +21,7 @@
-->
<template>
<AppSidebar :title="form.event.title" class="forms-sidebar">
<AppSidebar :title="form.event.title">
<div class="configBox ">
<label class="title icon-settings">
{{ t('forms', 'Form configurations') }}
@ -209,27 +209,22 @@ export default {
<style lang="scss" scoped>
.forms-sidebar {
margin-top: 45px;
width: 25%;
.configBox {
.configBox {
display: flex;
flex-direction: column;
padding: 8px;
& > * {
padding-left: 21px;
}
& > .title {
display: flex;
flex-direction: column;
padding: 8px;
& > * {
padding-left: 21px;
}
& > .title {
display: flex;
background-position: 0 2px;
padding-left: 24px;
opacity: 0.7;
font-weight: bold;
margin-bottom: 4px;
& > span {
padding-left: 4px;
}
background-position: 0 2px;
padding-left: 24px;
opacity: 0.7;
font-weight: bold;
margin-bottom: 4px;
& > span {
padding-left: 4px;
}
}
}