Sidebar & cleanup 2
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
parent
37ee5fcea6
commit
1cba7bbb5a
37
package-lock.json
generated
37
package-lock.json
generated
|
@ -1722,17 +1722,18 @@
|
|||
}
|
||||
},
|
||||
"@nextcloud/l10n": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-1.0.0.tgz",
|
||||
"integrity": "sha512-A6zUwFWgwvQ5q86GdcfgD2t3uZ+H/k45S0OUlS6iMf0p5br9IrovF7hdv5+jaJcdQpkpOX2AKuordC1KheNGGA==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-1.1.1.tgz",
|
||||
"integrity": "sha512-aVGvOhXgBzGtgMTOIVov1T7jt35FjX2vCqWCT0C/t6T6VgRn6l2Wb3/jzQycyUgGe71VPqS/4TpSH21yyH99YA==",
|
||||
"requires": {
|
||||
"core-js": "3.5.0"
|
||||
"core-js": "3.6.4",
|
||||
"node-gettext": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz",
|
||||
"integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw=="
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
||||
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1745,6 +1746,23 @@
|
|||
"core-js": "3.6.1",
|
||||
"i18next": "19.0.2",
|
||||
"moment": "2.24.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextcloud/l10n": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-1.0.0.tgz",
|
||||
"integrity": "sha512-A6zUwFWgwvQ5q86GdcfgD2t3uZ+H/k45S0OUlS6iMf0p5br9IrovF7hdv5+jaJcdQpkpOX2AKuordC1KheNGGA==",
|
||||
"requires": {
|
||||
"core-js": "3.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz",
|
||||
"integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nextcloud/router": {
|
||||
|
@ -3775,6 +3793,11 @@
|
|||
"integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=",
|
||||
"dev": true
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
|
||||
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
|
|
|
@ -69,11 +69,14 @@
|
|||
"test:coverage": "jest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextcloud/auth": "^1.2.2",
|
||||
"@nextcloud/axios": "^1.3.2",
|
||||
"@nextcloud/dialogs": "^1.2.2",
|
||||
"@nextcloud/l10n": "^1.1.1",
|
||||
"@nextcloud/moment": "^1.1.0",
|
||||
"@nextcloud/router": "^1.0.2",
|
||||
"@nextcloud/vue": "^1.4.1",
|
||||
"debounce": "^1.2.0",
|
||||
"json2csv": "5.0.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</AppNavigation>
|
||||
|
||||
<!-- No forms & loading emptycontents -->
|
||||
<AppContent v-if="loading || noForms || !hash">
|
||||
<AppContent v-if="loading || noForms || (!hash && $route.name !== 'create')">
|
||||
<EmptyContent v-if="loading" icon="icon-loading">
|
||||
{{ t('forms', 'Loading forms …') }}
|
||||
</EmptyContent>
|
||||
|
@ -48,7 +48,10 @@
|
|||
</AppContent>
|
||||
|
||||
<!-- No errors show router content -->
|
||||
<router-view v-else />
|
||||
<template v-else>
|
||||
<router-view :form="selectedForm" />
|
||||
<router-view :form="selectedForm" name="sidebar" />
|
||||
</template>
|
||||
</Content>
|
||||
</template>
|
||||
|
||||
|
@ -97,6 +100,11 @@ export default {
|
|||
hash() {
|
||||
return this.$route.params.hash
|
||||
},
|
||||
|
||||
selectedForm() {
|
||||
// TODO: replace with form.hash
|
||||
return this.forms.find(form => form.event.hash === this.hash)
|
||||
},
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
:exact="true"
|
||||
:title="form.title"
|
||||
:to="{ name: 'edit', params: { hash: form.hash } }">
|
||||
<AppNavigationIconBullet slot="icon" :color="bulletColor" />
|
||||
<template #actions>
|
||||
<ActionRouter :close-after-click="true"
|
||||
:exact="true"
|
||||
|
@ -48,6 +49,7 @@
|
|||
|
||||
<script>
|
||||
import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem'
|
||||
import AppNavigationIconBullet from '@nextcloud/vue/dist/Components/AppNavigationIconBullet'
|
||||
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
||||
import ActionRouter from '@nextcloud/vue/dist/Components/ActionRouter'
|
||||
import ActionSeparator from '@nextcloud/vue/dist/Components/ActionSeparator'
|
||||
|
@ -57,6 +59,7 @@ export default {
|
|||
|
||||
components: {
|
||||
AppNavigationItem,
|
||||
AppNavigationIconBullet,
|
||||
ActionButton,
|
||||
ActionRouter,
|
||||
ActionSeparator,
|
||||
|
@ -69,6 +72,21 @@ export default {
|
|||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Map form state to bullet color
|
||||
*
|
||||
* @returns {string} hex color
|
||||
*/
|
||||
bulletColor() {
|
||||
const style = getComputedStyle(document.body)
|
||||
if (this.form.expired) {
|
||||
return style.getPropertyValue('--color-error').slice(-6)
|
||||
}
|
||||
return style.getPropertyValue('--color-success').slice(-6)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async deleteForm() {
|
||||
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @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 class="forms-sidebar">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.forms-sidebar {
|
||||
min-width: 300px;
|
||||
border-left: 1px solid var(--color-border);
|
||||
z-index: 500;
|
||||
> ul,
|
||||
> div {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -65,6 +65,7 @@
|
|||
|
||||
<script>
|
||||
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
|
||||
// TODO: replace with same design as core sharing
|
||||
import UserDiv from './_base-UserDiv'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
|
|
26
src/main.js
26
src/main.js
|
@ -20,37 +20,45 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import { generateFilePath } from '@nextcloud/router'
|
||||
import { getRequestToken } from '@nextcloud/auth'
|
||||
import { translate, translatePlural } from '@nextcloud/l10n'
|
||||
import Vue from 'vue'
|
||||
import router from './router'
|
||||
import Forms from './Forms'
|
||||
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
|
||||
|
||||
import router from './router'
|
||||
import Forms from './Forms'
|
||||
import Modal from './plugins/plugin.js'
|
||||
|
||||
// TODO: not use global registration
|
||||
Vue.directive('tooltip', Tooltip)
|
||||
|
||||
Vue.use(VueClipboard)
|
||||
Vue.use(Modal)
|
||||
|
||||
Vue.prototype.t = t
|
||||
Vue.prototype.n = n
|
||||
Vue.prototype.t = translate
|
||||
Vue.prototype.n = translatePlural
|
||||
|
||||
// TODO: see if necessary
|
||||
Vue.prototype.OC = OC
|
||||
Vue.prototype.OCA = OCA
|
||||
|
||||
// CSP config for webpack dynamic chunk loading
|
||||
// eslint-disable-next-line
|
||||
__webpack_nonce__ = btoa(OC.requestToken)
|
||||
__webpack_nonce__ = btoa(getRequestToken())
|
||||
|
||||
// Correct the root of the app for chunk loading
|
||||
// OC.linkTo matches the apps folders
|
||||
// OC.generateUrl ensure the index.php (or not)
|
||||
// We do not want the index.php since we're loading files
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = OC.linkTo('forms', 'js/')
|
||||
__webpack_public_path__ = generateFilePath('forms', '', 'js/')
|
||||
|
||||
/* eslint-disable-next-line no-new */
|
||||
new Vue({
|
||||
el: '#content',
|
||||
router: router,
|
||||
// eslint-disable-next-line vue/match-component-file-name
|
||||
name: 'FormsRoot',
|
||||
router,
|
||||
render: h => h(Forms),
|
||||
})
|
||||
|
|
|
@ -25,5 +25,10 @@ export default {
|
|||
type: String,
|
||||
default: null,
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
// TODO: use default Form object ?
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import { generateUrl } from '@nextcloud/router'
|
|||
|
||||
import Create from './views/Create'
|
||||
import Results from './views/Results'
|
||||
import Sidebar from './views/Sidebar'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
|
@ -46,7 +47,10 @@ export default new Router({
|
|||
},
|
||||
{
|
||||
path: '/new',
|
||||
component: Create,
|
||||
components: {
|
||||
default: Create,
|
||||
sidebar: Sidebar,
|
||||
},
|
||||
name: 'create',
|
||||
},
|
||||
{
|
||||
|
@ -56,7 +60,10 @@ export default new Router({
|
|||
},
|
||||
{
|
||||
path: '/:hash/edit',
|
||||
component: Create,
|
||||
components: {
|
||||
default: Create,
|
||||
sidebar: Sidebar,
|
||||
},
|
||||
name: 'edit',
|
||||
props: true,
|
||||
},
|
||||
|
@ -68,7 +75,10 @@ export default new Router({
|
|||
},
|
||||
{
|
||||
path: '/:hash/clone',
|
||||
component: Create,
|
||||
components: {
|
||||
default: Create,
|
||||
sidebar: Sidebar,
|
||||
},
|
||||
name: 'clone',
|
||||
props: true,
|
||||
},
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<AppContent>
|
||||
<Controls :intitle="title">
|
||||
<template slot="after">
|
||||
<button :disabled="writingForm" class="button btn primary" @click="writeForm(form.mode)">
|
||||
<button :disabled="writingForm" class="button btn primary" @click="debounceWriteForm">
|
||||
<span>{{ saveButtonTitle }}</span>
|
||||
<span v-if="writingForm" class="icon-loading-small" />
|
||||
</button>
|
||||
|
@ -89,135 +89,19 @@
|
|||
</transitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SideBar v-if="sidebar">
|
||||
<div v-if="adminMode" class="warning">
|
||||
{{ t('forms', 'You are editing in admin mode') }}
|
||||
</div>
|
||||
<UserDiv :user-id="form.event.owner" :description="t('forms', 'Owner')" />
|
||||
|
||||
<ul class="tabHeaders">
|
||||
<li class="tabHeader selected" data-tabid="configurationsTabView" data-tabindex="0">
|
||||
<a href="#">
|
||||
{{ t('forms', 'Configuration') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="protect">
|
||||
<span>{{ t('forms', 'Configuration is locked. Changing options may result in unwanted behaviour, but you can unlock it anyway.') }}</span>
|
||||
<button @click="protect=false">
|
||||
{{ t('forms', 'Unlock configuration ') }}
|
||||
</button>
|
||||
</div>
|
||||
<div id="configurationsTabView" class="tab">
|
||||
<div class="configBox ">
|
||||
<label class="title icon-settings">
|
||||
{{ t('forms', 'Form configurations') }}
|
||||
</label>
|
||||
|
||||
<input id="anonymous"
|
||||
v-model="form.event.isAnonymous"
|
||||
:disabled="protect"
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label for="anonymous" class="title">
|
||||
{{ t('forms', 'Anonymous form') }}
|
||||
</label>
|
||||
|
||||
<input id="unique"
|
||||
v-model="form.event.unique"
|
||||
:disabled="form.event.access !== 'registered' || form.event.isAnonymous"
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label for="unique" class="title">
|
||||
<span>{{ t('forms', 'Only allow one submission per user') }}</span>
|
||||
</label>
|
||||
|
||||
<input v-show="form.event.isAnonymous"
|
||||
id="trueAnonymous"
|
||||
v-model="form.event.fullAnonymous"
|
||||
:disabled="protect"
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<input id="expiration"
|
||||
v-model="form.event.expiration"
|
||||
:disabled="protect"
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label class="title" for="expiration">
|
||||
{{ t('forms', 'Expires') }}
|
||||
</label>
|
||||
|
||||
<DatetimePicker v-show="form.event.expiration"
|
||||
v-model="form.event.expirationDate"
|
||||
v-bind="expirationDatePicker"
|
||||
:disabled="protect"
|
||||
:time-picker-options="{ start: '00:00', step: '00:05', end: '23:55' }"
|
||||
style="width:170px" />
|
||||
</div>
|
||||
|
||||
<div class="configBox">
|
||||
<label class="title icon-user">
|
||||
{{ t('forms', 'Access') }}
|
||||
</label>
|
||||
<input id="private"
|
||||
v-model="form.event.access"
|
||||
:disabled="protect"
|
||||
type="radio"
|
||||
value="registered"
|
||||
class="radio">
|
||||
<label for="private" class="title">
|
||||
<div class="title icon-group" />
|
||||
<span>{{ t('forms', 'Registered users only') }}</span>
|
||||
</label>
|
||||
<input id="public"
|
||||
v-model="form.event.access"
|
||||
:disabled="protect"
|
||||
type="radio"
|
||||
value="public"
|
||||
class="radio">
|
||||
<label for="public" class="title">
|
||||
<div class="title icon-link" />
|
||||
<span>{{ t('forms', 'Public access') }}</span>
|
||||
</label>
|
||||
<input id="select"
|
||||
v-model="form.event.access"
|
||||
:disabled="protect"
|
||||
type="radio"
|
||||
value="select"
|
||||
class="radio">
|
||||
<label for="select" class="title">
|
||||
<div class="title icon-shared" />
|
||||
<span>{{ t('forms', 'Only shared') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ShareDiv v-show="form.event.access === 'select'"
|
||||
:active-shares="form.shares"
|
||||
:placeholder="t('forms', 'Name of user or group')"
|
||||
:hide-names="true"
|
||||
@update-shares="updateShares"
|
||||
@remove-share="removeShare" />
|
||||
</SideBar>
|
||||
<LoadingOverlay v-if="loadingForm" />
|
||||
</AppContent>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from '@nextcloud/axios'
|
||||
import moment from '@nextcloud/moment'
|
||||
import debounce from 'debounce'
|
||||
|
||||
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
|
||||
import DatetimePicker from '@nextcloud/vue/dist/Components/DatetimePicker'
|
||||
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||
|
||||
import Controls from '../components/_base-Controls'
|
||||
import LoadingOverlay from '../components/_base-LoadingOverlay'
|
||||
import QuizFormItem from '../components/quizFormItem'
|
||||
import ShareDiv from '../components/shareDiv'
|
||||
import SideBar from '../components/_base-SideBar'
|
||||
import UserDiv from '../components/_base-UserDiv'
|
||||
|
||||
import ViewsMixin from '../mixins/ViewsMixin'
|
||||
|
||||
|
@ -226,65 +110,21 @@ export default {
|
|||
components: {
|
||||
AppContent,
|
||||
Controls,
|
||||
DatetimePicker,
|
||||
LoadingOverlay,
|
||||
QuizFormItem,
|
||||
ShareDiv,
|
||||
SideBar,
|
||||
UserDiv,
|
||||
},
|
||||
|
||||
mixins: [ViewsMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
move: {
|
||||
step: 1,
|
||||
unit: 'week',
|
||||
units: ['minute', 'hour', 'day', 'week', 'month', 'year'],
|
||||
},
|
||||
form: {
|
||||
mode: 'create',
|
||||
votes: [],
|
||||
shares: [],
|
||||
grantedAs: 'owner',
|
||||
id: 0,
|
||||
result: 'new',
|
||||
event: {
|
||||
id: 0,
|
||||
hash: '',
|
||||
type: 'quizForm',
|
||||
title: '',
|
||||
description: '',
|
||||
created: '',
|
||||
access: 'public',
|
||||
unique: false,
|
||||
expiration: false,
|
||||
expirationDate: '',
|
||||
expired: false,
|
||||
isAnonymous: false,
|
||||
fullAnonymous: false,
|
||||
owner: undefined,
|
||||
},
|
||||
options: {
|
||||
formQuizQuestions: [],
|
||||
},
|
||||
},
|
||||
lang: '',
|
||||
locale: '',
|
||||
placeholder: '',
|
||||
newQuizAnswer: '',
|
||||
newQuizQuestion: '',
|
||||
nextQuizAnswerId: 1,
|
||||
nextQuizQuestionId: 1,
|
||||
protect: false,
|
||||
writingForm: false,
|
||||
loadingForm: true,
|
||||
sidebar: false,
|
||||
titleEmpty: false,
|
||||
indexPage: '',
|
||||
longDateFormat: '',
|
||||
dateTimeFormat: '',
|
||||
selected: '',
|
||||
uniqueName: false,
|
||||
uniqueAns: false,
|
||||
|
@ -300,10 +140,6 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
adminMode() {
|
||||
return (this.form.event.owner !== OC.getCurrentUser().uid && OC.isUserAdmin())
|
||||
},
|
||||
|
||||
langShort() {
|
||||
return this.lang.split('-')[0]
|
||||
},
|
||||
|
@ -331,38 +167,6 @@ export default {
|
|||
return moment.localeData(moment.locale(this.locale))
|
||||
},
|
||||
|
||||
expirationDatePicker() {
|
||||
return {
|
||||
editable: true,
|
||||
minuteStep: 1,
|
||||
type: 'datetime',
|
||||
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
|
||||
lang: this.lang.split('-')[0],
|
||||
placeholder: t('forms', 'Expiration date'),
|
||||
timePickerOptions: {
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
optionDatePicker() {
|
||||
return {
|
||||
editable: false,
|
||||
minuteStep: 1,
|
||||
type: 'datetime',
|
||||
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
|
||||
lang: this.lang.split('-')[0],
|
||||
placeholder: t('forms', 'Click to add a date'),
|
||||
timePickerOptions: {
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -370,37 +174,25 @@ export default {
|
|||
// only used when the title changes after page load
|
||||
document.title = t('forms', 'Forms') + ' - ' + this.title
|
||||
},
|
||||
|
||||
form: {
|
||||
deep: true,
|
||||
handler: function() {
|
||||
this.debounceWriteForm()
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.indexPage = OC.generateUrl('apps/forms/')
|
||||
this.lang = OC.getLanguage()
|
||||
try {
|
||||
this.locale = OC.getLocale()
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
this.locale = this.lang
|
||||
} else {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
moment.locale(this.locale)
|
||||
this.longDateFormat = moment.localeData().longDateFormat('L')
|
||||
this.dateTimeFormat = moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT')
|
||||
|
||||
if (this.$route.name === 'create') {
|
||||
// TODO: manage this from Forms.vue, request a new form to the server
|
||||
this.form.event.owner = OC.getCurrentUser().uid
|
||||
this.loadingForm = false
|
||||
} else if (this.$route.name === 'edit') {
|
||||
this.loadForm(this.$route.params.hash)
|
||||
this.protect = true
|
||||
// TODO: fetch & update form?
|
||||
this.form.mode = 'edit'
|
||||
} else if (this.$route.name === 'clone') {
|
||||
this.loadForm(this.$route.params.hash)
|
||||
}
|
||||
if (window.innerWidth > 1024) {
|
||||
this.sidebar = true
|
||||
// TODO: CLONE
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -410,18 +202,6 @@ export default {
|
|||
this.sidebar = !this.sidebar
|
||||
},
|
||||
|
||||
addShare(item) {
|
||||
this.form.shares.push(item)
|
||||
},
|
||||
|
||||
updateShares(share) {
|
||||
this.form.shares = share.slice(0)
|
||||
},
|
||||
|
||||
removeShare(item) {
|
||||
this.form.shares.splice(this.form.shares.indexOf(item), 1)
|
||||
},
|
||||
|
||||
checkNames() {
|
||||
this.uniqueName = true
|
||||
this.form.options.formQuizQuestions.forEach(q => {
|
||||
|
@ -434,9 +214,9 @@ export default {
|
|||
addQuestion() {
|
||||
this.checkNames()
|
||||
if (this.selected === '') {
|
||||
OC.Notification.showTemporary(t('forms', 'Select a question type!'))
|
||||
showError(t('forms', 'Select a question type!'), { duration: 3000 })
|
||||
} else if (!this.uniqueName) {
|
||||
OC.Notification.showTemporary(t('forms', 'Cannot have the same question!'))
|
||||
showError(t('forms', 'Cannot have the same question!'))
|
||||
} else {
|
||||
if (this.newQuizQuestion !== null & this.newQuizQuestion !== '' & (/\S/.test(this.newQuizQuestion))) {
|
||||
this.form.options.formQuizQuestions.push({
|
||||
|
@ -467,7 +247,7 @@ export default {
|
|||
addAnswer(item, question) {
|
||||
this.checkAnsNames(item, question)
|
||||
if (!this.uniqueAnsName) {
|
||||
OC.Notification.showTemporary(t('forms', 'Two answers cannot be the same!'))
|
||||
showError(t('forms', 'Two answers cannot be the same!'), { duration: 3000 })
|
||||
} else {
|
||||
if (item.newQuizAnswer !== null & item.newQuizAnswer !== '' & (/\S/.test(item.newQuizAnswer))) {
|
||||
item.formQuizAnswers.push({
|
||||
|
@ -493,24 +273,24 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
writeForm(mode) {
|
||||
debounceWriteForm: debounce(function() {
|
||||
this.writeForm()
|
||||
}, 200),
|
||||
|
||||
writeForm() {
|
||||
this.allHaveAns()
|
||||
if (mode !== '') {
|
||||
this.form.mode = mode
|
||||
}
|
||||
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!'))
|
||||
showError(t('forms', 'Title must not be empty!'), { duration: 3000 })
|
||||
} else if (this.form.options.formQuizQuestions.length === 0) {
|
||||
OC.Notification.showTemporary(t('forms', 'Must have at least one question!'))
|
||||
showError(t('forms', 'Must have at least one question!'), { duration: 3000 })
|
||||
} else if (!this.haveAns) {
|
||||
OC.Notification.showTemporary(t('forms', 'All questions need answers!'))
|
||||
showError(t('forms', 'All questions need answers!'), { duration: 3000 })
|
||||
} else if (this.form.event.expiration & this.form.event.expirationDate === '') {
|
||||
OC.Notification.showTemporary(t('forms', 'Need to pick an expiration date!'))
|
||||
showError(t('forms', 'Need to pick an expiration date!'), { duration: 3000 })
|
||||
} else {
|
||||
this.writingForm = true
|
||||
this.titleEmpty = false
|
||||
// this.form.event.expirationDate = moment(this.form.event.expirationDate).utc()
|
||||
|
||||
axios.post(OC.generateUrl('apps/forms/write/form'), this.form)
|
||||
.then((response) => {
|
||||
|
@ -518,201 +298,131 @@ export default {
|
|||
this.form.event.hash = response.data.hash
|
||||
this.form.event.id = response.data.id
|
||||
this.writingForm = false
|
||||
OC.Notification.showTemporary(t('forms', '%n successfully saved', 1, this.form.event.title))
|
||||
// window.location.href = OC.generateUrl('apps/forms/edit/' + this.form.event.hash)
|
||||
this.$router.push('/apps/forms/')
|
||||
showSuccess(t('forms', '%n successfully saved', 1, this.form.event.title), { duration: 3000 })
|
||||
}, (error) => {
|
||||
this.form.event.hash = ''
|
||||
this.writingForm = false
|
||||
OC.Notification.showTemporary(t('forms', 'Error on saving form, see console'))
|
||||
showError(t('forms', 'Error on saving form, see console'))
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(error.response)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
loadForm(hash) {
|
||||
this.loadingForm = true
|
||||
axios.get(OC.generateUrl('apps/forms/get/form/' + hash))
|
||||
.then((response) => {
|
||||
this.form = response.data
|
||||
if (this.form.event.expirationDate !== null) {
|
||||
this.form.event.expirationDate = new Date(moment.utc(this.form.event.expirationDate))
|
||||
} else {
|
||||
this.form.event.expirationDate = ''
|
||||
}
|
||||
|
||||
if (this.$route.name === 'clone') {
|
||||
this.form.event.owner = OC.getCurrentUser().uid
|
||||
this.form.event.title = t('forms', 'Clone of %n', 1, this.form.event.title)
|
||||
this.form.event.id = 0
|
||||
this.form.id = 0
|
||||
this.form.event.hash = ''
|
||||
this.form.grantedAs = 'owner'
|
||||
this.form.result = 'new'
|
||||
this.form.mode = 'create'
|
||||
this.form.votes = []
|
||||
}
|
||||
|
||||
this.loadingForm = false
|
||||
this.newQuizAnswer = ''
|
||||
this.newQuizQuestion = ''
|
||||
|
||||
}, (error) => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(error.response)
|
||||
this.form.event.hash = ''
|
||||
this.loadingForm = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#app-content {
|
||||
input.hasTimepicker {
|
||||
width: 75px;
|
||||
}
|
||||
input.hasTimepicker {
|
||||
width: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--color-error);
|
||||
font-weight: bold;
|
||||
color: var(--color-error);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.forms-content {
|
||||
display: flex;
|
||||
padding-top: 45px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
padding-top: 45px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.workbench {
|
||||
margin-top: 45px;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
overflow-x: hidden;
|
||||
margin-top: 45px;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
overflow-x: hidden;
|
||||
|
||||
> div {
|
||||
min-width: 245px;
|
||||
max-width: 540px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.forms-sidebar {
|
||||
margin-top: 45px;
|
||||
width: 25%;
|
||||
|
||||
.configBox {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
&.error {
|
||||
border: 2px solid var(--color-error);
|
||||
box-shadow: 1px 0 var(--border-radius) var(--color-box-shadow);
|
||||
}
|
||||
> div {
|
||||
min-width: 245px;
|
||||
max-width: 540px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transitions for inserting and removing list items */
|
||||
.list-enter-active,
|
||||
.list-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.list-enter,
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.list-move {
|
||||
transition: transform 0.5s;
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
/* */
|
||||
|
||||
#form-item-selector-text {
|
||||
> input {
|
||||
width: 100%;
|
||||
}
|
||||
> input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-table {
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
line-height: 24px;
|
||||
min-height: 24px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
line-height: 24px;
|
||||
min-height: 24px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
transition: var(--background-dark) 0.3s ease;
|
||||
background-color: var(--color-background-dark); //$hover-color;
|
||||
&:active,
|
||||
&:hover {
|
||||
transition: var(--background-dark) 0.3s ease;
|
||||
background-color: var(--color-background-dark); //$hover-color;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
font-size: 1.2em;
|
||||
opacity: 0.7;
|
||||
white-space: normal;
|
||||
padding-right: 4px;
|
||||
&.avatar {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
> div {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
font-size: 1.2em;
|
||||
opacity: 0.7;
|
||||
white-space: normal;
|
||||
padding-right: 4px;
|
||||
&.avatar {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> div:nth-last-child(1) {
|
||||
justify-content: center;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
> div:nth-last-child(1) {
|
||||
justify-content: center;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
&.button-inline {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
&.button-inline {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.selectUnit {
|
||||
display: flex;
|
||||
|
|
244
src/views/Sidebar.vue
Normal file
244
src/views/Sidebar.vue
Normal file
|
@ -0,0 +1,244 @@
|
|||
<!--
|
||||
- @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/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<AppSidebar :title="form.event.title" class="forms-sidebar">
|
||||
<div class="configBox ">
|
||||
<label class="title icon-settings">
|
||||
{{ t('forms', 'Form configurations') }}
|
||||
</label>
|
||||
|
||||
<input id="anonymous"
|
||||
v-model="form.event.isAnonymous"
|
||||
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label for="anonymous" class="title">
|
||||
{{ t('forms', 'Anonymous form') }}
|
||||
</label>
|
||||
|
||||
<input id="unique"
|
||||
v-model="form.event.unique"
|
||||
:disabled="form.event.access !== 'registered' || form.event.isAnonymous"
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label for="unique" class="title">
|
||||
<span>{{ t('forms', 'Only allow one submission per user') }}</span>
|
||||
</label>
|
||||
|
||||
<input v-show="form.event.isAnonymous"
|
||||
id="trueAnonymous"
|
||||
v-model="form.event.fullAnonymous"
|
||||
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<input id="expiration"
|
||||
v-model="form.event.expiration"
|
||||
|
||||
type="checkbox"
|
||||
class="checkbox">
|
||||
<label class="title" for="expiration">
|
||||
{{ t('forms', 'Expires') }}
|
||||
</label>
|
||||
|
||||
<DatetimePicker v-show="form.event.expiration"
|
||||
v-model="form.event.expirationDate"
|
||||
v-bind="expirationDatePicker"
|
||||
|
||||
:time-picker-options="{ start: '00:00', step: '00:05', end: '23:55' }"
|
||||
style="width:170px" />
|
||||
</div>
|
||||
|
||||
<div class="configBox">
|
||||
<label class="title icon-user">
|
||||
{{ t('forms', 'Access') }}
|
||||
</label>
|
||||
<input id="private"
|
||||
v-model="form.event.access"
|
||||
|
||||
type="radio"
|
||||
value="registered"
|
||||
class="radio">
|
||||
<label for="private" class="title">
|
||||
<div class="title icon-group" />
|
||||
<span>{{ t('forms', 'Registered users only') }}</span>
|
||||
</label>
|
||||
<input id="public"
|
||||
v-model="form.event.access"
|
||||
|
||||
type="radio"
|
||||
value="public"
|
||||
class="radio">
|
||||
<label for="public" class="title">
|
||||
<div class="title icon-link" />
|
||||
<span>{{ t('forms', 'Public access') }}</span>
|
||||
</label>
|
||||
<input id="select"
|
||||
v-model="form.event.access"
|
||||
|
||||
type="radio"
|
||||
value="select"
|
||||
class="radio">
|
||||
<label for="select" class="title">
|
||||
<div class="title icon-shared" />
|
||||
<span>{{ t('forms', 'Only shared') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<ShareDiv v-show="form.event.access === 'select'"
|
||||
:active-shares="form.shares"
|
||||
:placeholder="t('forms', 'Name of user or group')"
|
||||
:hide-names="true"
|
||||
@update-shares="updateShares"
|
||||
@remove-share="removeShare" />
|
||||
</AppSidebar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppSidebar from '@nextcloud/vue/dist/Components/AppSidebar'
|
||||
import DatetimePicker from '@nextcloud/vue/dist/Components/DatetimePicker'
|
||||
import moment from '@nextcloud/moment'
|
||||
|
||||
import ShareDiv from '../components/shareDiv'
|
||||
import ViewsMixin from '../mixins/ViewsMixin'
|
||||
|
||||
export default {
|
||||
name: 'Sidebar',
|
||||
|
||||
components: {
|
||||
AppSidebar,
|
||||
DatetimePicker,
|
||||
ShareDiv,
|
||||
},
|
||||
mixins: [ViewsMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
lang: '',
|
||||
locale: '',
|
||||
longDateFormat: '',
|
||||
dateTimeFormat: '',
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
expirationDatePicker() {
|
||||
return {
|
||||
editable: true,
|
||||
minuteStep: 1,
|
||||
type: 'datetime',
|
||||
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
|
||||
lang: this.lang.split('-')[0],
|
||||
placeholder: t('forms', 'Expiration date'),
|
||||
timePickerOptions: {
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
optionDatePicker() {
|
||||
return {
|
||||
editable: false,
|
||||
minuteStep: 1,
|
||||
type: 'datetime',
|
||||
format: moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT'),
|
||||
lang: this.lang.split('-')[0],
|
||||
placeholder: t('forms', 'Click to add a date'),
|
||||
timePickerOptions: {
|
||||
start: '00:00',
|
||||
step: '00:30',
|
||||
end: '23:30',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.lang = OC.getLanguage()
|
||||
try {
|
||||
this.locale = OC.getLocale()
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
this.locale = this.lang
|
||||
} else {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
moment.locale(this.locale)
|
||||
this.longDateFormat = moment.localeData().longDateFormat('L')
|
||||
this.dateTimeFormat = moment.localeData().longDateFormat('L') + ' ' + moment.localeData().longDateFormat('LT')
|
||||
},
|
||||
|
||||
methods: {
|
||||
addShare(item) {
|
||||
this.form.shares.push(item)
|
||||
},
|
||||
|
||||
updateShares(share) {
|
||||
this.form.shares = share.slice(0)
|
||||
},
|
||||
|
||||
removeShare(item) {
|
||||
this.form.shares.splice(this.form.shares.indexOf(item), 1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.forms-sidebar {
|
||||
margin-top: 45px;
|
||||
width: 25%;
|
||||
|
||||
.configBox {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
&.error {
|
||||
border: 2px solid var(--color-error);
|
||||
box-shadow: 1px 0 var(--border-radius) var(--color-box-shadow);
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue