diff options
author | Julius Härtl <jus@bitgrid.net> | 2020-09-03 17:35:02 +0300 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2020-09-03 17:35:02 +0300 |
commit | 2f63be0a13c4388f7625c87078849abcf10e89d5 (patch) | |
tree | a09c3837181b9077f6c5cba968fc31dc9b18c306 /src | |
parent | 7419f56fafaf40574ba28f3dc794ad8be99d951e (diff) |
Some code refactoring
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/App.vue | 390 | ||||
-rw-r--r-- | src/components/AppSelector.vue (renamed from src/components/appselector.vue) | 73 | ||||
-rw-r--r-- | src/components/DetailSection.vue | 92 | ||||
-rw-r--r-- | src/components/detailsection.vue | 85 | ||||
-rw-r--r-- | src/main.js | 18 | ||||
-rw-r--r-- | src/webpack.config.js | 42 |
6 files changed, 360 insertions, 340 deletions
diff --git a/src/App.vue b/src/App.vue index 2509d77..a3ff23a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,204 +1,212 @@ <template> <div id="issuetemplate" class="section"> - <h2 class="inlineblock">{{ t('issuetemplate', 'Issue reporting') }}</h2> - - <p> - For reporting potential security issues please see - <a href="https://nextcloud.com/security/">https://nextcloud.com/security/</a> - </p> - - <div> - - <form-wizard @on-complete="onComplete" - shape="tab" - color="#0082c9" - error-color="#a94442" - ref="wizard"> - <div slot="title"></div> - - - <tab-content title="Affected component" icon="icon-category-customization icon-invert" :before-change="validateAppSelect"> - <app-selector v-on:select="selectComponent"></app-selector> - </tab-content> - - <tab-content title="Issue description" icon="icon-user icon-invert" :before-change="validateIssueDescription"> - <vue-form-generator :model="model" :schema="firstTabSchema" :options="formOptions" ref="firstTabForm"></vue-form-generator> - </tab-content> - - <tab-content v-for="tab in tabs" v-if="model.component" :key="tab.identifier" :title="tab.title" icon="icon-settings icon-invert" :before-change="()=>validateDetails(tab)"> - <detail-section :app="getAppId()" :section="tab.identifier" :ref="tab.identifier" :model="model"></detail-section> - </tab-content> - - <tab-content title="Check issue report" icon="icon-checkmark icon-invert" v-if="tabs.length"> - <h4>Check your bug report before submitting it</h4> - <div class="panel-body"> - <p> - <strong>Please always check if the automatically filled out information is correct and there is nothing important missing, before reporting the issue.</strong> - </p> - - <p> - <strong>This report will be submitted to nextcloud/server</strong> - </p> - - <div id="preview" v-html="preview.rendered"> - + <h2 class="inlineblock"> + {{ t('issuetemplate', 'Issue reporting') }} + </h2> + + <p> + For reporting potential security issues please see + <a href="https://nextcloud.com/security/">https://nextcloud.com/security/</a> + </p> + + <div> + <form-wizard ref="wizard" + shape="tab" + color="#0082c9" + error-color="#a94442" + @on-complete="onComplete"> + <div slot="title" /> + + <tab-content title="Affected component" icon="icon-category-customization icon-invert" :before-change="validateAppSelect"> + <app-selector @select="selectComponent" /> + </tab-content> + + <tab-content title="Issue description" icon="icon-user icon-invert" :before-change="validateIssueDescription"> + <vue-form-generator ref="firstTabForm" + :model="model" + :schema="firstTabSchema" + :options="formOptions" /> + </tab-content> + + <tab-content v-for="tab in tabs" + v-show="model.component" + :key="tab.identifier" + :title="tab.title" + icon="icon-settings icon-invert" + :before-change="()=>validateDetails(tab)"> + <detail-section :ref="tab.identifier" + :app="getAppId()" + :section="tab.identifier" + :model="model" /> + </tab-content> + + <tab-content v-if="tabs.length" title="Check issue report" icon="icon-checkmark icon-invert"> + <h4>Check your bug report before submitting it</h4> + <div class="panel-body"> + <p> + <strong>Please always check if the automatically filled out information is correct and there is nothing important missing, before reporting the issue.</strong> + </p> + + <p> + <strong>This report will be submitted to nextcloud/server</strong> + </p> + + <div id="preview" v-html="preview.rendered" /> + <textarea id="preview" v-model="preview.markdown" /> </div> - <textarea id="preview" v-model="preview.markdown"> - - </textarea> - </div> - </tab-content> + </tab-content> - <template slot="footer" slot-scope="props"> - <div class="wizard-footer-left"> - <button v-if="props.activeTabIndex > 0 && !props.isLastStep" @click="props.prevTab()">Previous</button> - </div> - <div class="wizard-footer-right"> - <button v-if="!props.isLastStep" @click="props.nextTab()" class="primary">Next</button> - <button v-if="props.isLastStep" v-clipboard:copy="preview.markdown" class="">Copy issue text</button> - <button v-if="props.isLastStep && preview.markdown && preview.markdown.length<4096" @click="openIssue()" class="">Open a new issue</button> - </div> - </template> - - </form-wizard> - - - </div> + <template slot="footer" slot-scope="props"> + <div class="wizard-footer-left"> + <button v-if="props.activeTabIndex > 0 && !props.isLastStep" @click="props.prevTab()"> + Previous + </button> + </div> + <div class="wizard-footer-right"> + <button v-if="!props.isLastStep" class="primary" @click="props.nextTab()"> + Next + </button> + <button v-if="props.isLastStep" v-clipboard:copy="preview.markdown" class=""> + Copy issue text + </button> + <button v-if="props.isLastStep && preview.markdown && preview.markdown.length<4096" class="" @click="openIssue()"> + Open a new issue + </button> + </div> + </template> + </form-wizard> + </div> </div> </template> <script> - import AppSelector from './components/appselector.vue'; - import DetailSection from './components/detailsection.vue'; - import VueFormGenerator from 'vue-form-generator'; - - export default { - name: 'App', - components: { - 'app-selector': AppSelector, - 'detail-section': DetailSection, +import axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' +import VueFormGenerator from 'vue-form-generator' + +import AppSelector from './components/AppSelector.vue' +import DetailSection from './components/DetailSection.vue' + +export default { + name: 'App', + components: { + 'app-selector': AppSelector, + 'detail-section': DetailSection, + }, + data() { + return { + color: '#aaaaaa', + tabs: [], + model: {}, + preview: {}, + formOptions: { + validationErrorClass: 'has-error', + validationSuccessClass: 'has-success', + validateAfterChanged: true, + }, + firstTabSchema: { + fields: [ + { + type: 'input', + inputType: 'text', + label: 'Issue title', + model: 'title', + // required: true, + validator: VueFormGenerator.validators.string, + styleClasses: 'col-sm-12', + }, + { + type: 'textArea', + inputType: 'text', + label: 'Steps to reproduce', + model: 'stepsToReproduce', + required: true, + validator: VueFormGenerator.validators.string, + styleClasses: 'col-sm-12', + }, + { + type: 'textArea', + inputType: 'text', + label: 'Expected behaviour', + model: 'expectedBehaviour', + required: true, + validator: VueFormGenerator.validators.string, + styleClasses: 'col-sm-6', + }, + { + type: 'textArea', + inputType: 'text', + label: 'Actual behaviour', + model: 'actualBehaviour', + required: true, + validator: VueFormGenerator.validators.string, + styleClasses: 'col-sm-6', + }, + ], + }, + } + }, + mounted() { + this.resetWizard() + }, + methods: { + async updateSections() { + this.tabs = [] + try { + const { data } = await axios.get(generateUrl('/apps/issuetemplate/sections/' + this.getAppId())) + this.tabs = data + } catch (e) { + console.error(e) + this.tabs = [] + } + }, + getAppId() { + if (this.model.component !== null) { + return this.model.component.id + } + return null }, - mounted: function () { - this.resetWizard(); + selectComponent(component) { + this.model.component = component + this.updateSections() + this.$refs.wizard.nextTab() }, - data() { - return { - color: '#aaaaaa', - tabs: [], - model: {}, - preview: {}, - formOptions: { - validationErrorClass: "has-error", - validationSuccessClass: "has-success", - validateAfterChanged: true - }, - firstTabSchema: { - fields: [ - { - type: "input", - inputType: "text", - label: "Issue title", - model: "title", - //required: true, - validator: VueFormGenerator.validators.string, - styleClasses: 'col-sm-12' - }, - { - type: "textArea", - inputType: "text", - label: "Steps to reproduce", - model: "stepsToReproduce", - required: true, - validator: VueFormGenerator.validators.string, - styleClasses: 'col-sm-12' - }, - { - type: "textArea", - inputType: "text", - label: "Expected behaviour", - model: "expectedBehaviour", - required: true, - validator: VueFormGenerator.validators.string, - styleClasses: 'col-sm-6' - }, - { - type: "textArea", - inputType: "text", - label: "Actual behaviour", - model: "actualBehaviour", - required: true, - validator: VueFormGenerator.validators.string, - styleClasses: 'col-sm-6' - } - ] - } + onComplete() { + this.resetWizard() + }, + resetWizard() { + this.model = { + component: null, + title: '', + stepsToReproduce: '', + expectedBehaviour: '', + actualBehaviour: '', + details: {}, } }, - methods: { - updateSections: function () { - var self = this; - self.tabs = []; - $.ajax({ - url: OC.generateUrl('/apps/issuetemplate/sections/' + this.getAppId()), - method: 'GET', - success: function (data) { - self.tabs = data; - }, - error: function (error) { - self.tabs = []; - } - }); - }, - getAppId: function () { - if (this.model.component !== null) { - return this.model.component.id; - } - return null; - }, - selectComponent: function (component) { - this.model.component = component; - this.updateSections(); - this.$refs.wizard.nextTab(); - }, - onComplete: function(){ - this.resetWizard(); - }, - resetWizard: function() { - this.model = { - component: null, - title: '', - stepsToReproduce: '', - expectedBehaviour: '', - actualBehaviour: '', - details: {} - }; - }, - validateAppSelect: function() { - return this.getAppId() !== null; - }, - validateIssueDescription: function(){ - return this.$refs.firstTabForm.validate(); - }, - validateDetails: function(tab){ - var self = this; - var updates = this.$refs[tab.identifier][0].fetchUpdates(); - if (updates === false) { - return false; - } - this.model.details[tab.identifier] = updates[tab.identifier]; - $.ajax({ - url: OC.generateUrl('/apps/issuetemplate/render'), - data: this.model, - method: 'POST', - success: function (data) { - self.preview = data; - } - }); - return true; - }, - openIssue: function() { - var urlComplete = this.model.component.bugs + "/new/?title=" + encodeURIComponent(this.model.title) + "&body=" + encodeURIComponent(this.preview.markdown); - window.open(urlComplete); + validateAppSelect() { + return this.getAppId() !== null + }, + validateIssueDescription() { + return this.$refs.firstTabForm.validate() + }, + async validateDetails(tab) { + const updates = this.$refs[tab.identifier][0].fetchUpdates() + if (updates === false) { + return false } - } - } + this.model.details[tab.identifier] = updates[tab.identifier] + try { + const { data } = await axios.post(generateUrl('/apps/issuetemplate/render'), this.model) + this.preview = data + } catch (e) { + console.error(e) + } + return true + }, + openIssue() { + const urlComplete = this.model.component.bugs + '/new/?title=' + encodeURIComponent(this.model.title) + '&body=' + encodeURIComponent(this.preview.markdown) + window.open(urlComplete) + }, + }, +} </script> diff --git a/src/components/appselector.vue b/src/components/AppSelector.vue index 2a70660..a831ef6 100644 --- a/src/components/appselector.vue +++ b/src/components/AppSelector.vue @@ -21,45 +21,50 @@ --> <template> <div id="app-selector"> - <div v-for="item in apps"> - <h3>{{ item.title }}</h3> + <div v-for="item in apps" :key="item.id"> + <h3>{{ item.title }}</h3> - <div class="affected-components"> - <div class="affected-component" v-for="component in item.items" v-on:click="selectComponent(component)"> - <div class="logo"><img :src="component.icon" /></div> - <p>{{ component.name }}</p> + <div class="affected-components"> + <div v-for="component in item.items" + :key="component.id" + class="affected-component" + @click="selectComponent(component)"> + <div class="logo"> + <img :src="component.icon"> + </div> + <p>{{ component.name }}</p> + </div> </div> </div> - </div> </div> </template> <script> - export default { - methods: { - selectComponent: function(component) { - this.$emit('select', component); - } - }, - mounted: function () { - let self = this; - $.ajax({ - url: OC.generateUrl('/apps/issuetemplate/components'), - method: 'GET', - success: function (data) { - self.apps = data; - }, - error: function (error) { - console.log(error); - self.apps = []; - } - }); - }, - data: function () { - return { - apps: {}, - selected: this.selected, - } +import axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' + +export default { + name: 'AppSelector', + data() { + return { + apps: {}, + selected: this.selected, + } + }, + async mounted() { + try { + const response = await axios.get(generateUrl('/apps/issuetemplate/components')) + console.debug(response) + this.apps = response.data + } catch (e) { + console.error(e) + this.apps = [] } - } -</script>
\ No newline at end of file + }, + methods: { + selectComponent(component) { + this.$emit('select', component) + }, + }, +} +</script> diff --git a/src/components/DetailSection.vue b/src/components/DetailSection.vue new file mode 100644 index 0000000..e6998fe --- /dev/null +++ b/src/components/DetailSection.vue @@ -0,0 +1,92 @@ +<!-- + - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> + - + - @author Julius Härtl <jus@bitgrid.net> + - + - @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="details"> + <div class="details-section"> + <vue-form-generator :schema="schema" + :model="model" + :options="formOptions" + @validated="onValidated" /> + </div> + </div> +</template> + +<script> +import axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' + +export default { + props: { + app: { + type: String, + required: true, + }, + section: { + type: String, + required: true, + }, + }, + data() { + return { + isValid: false, + schema: {}, + model: {}, + formOptions: { + validateAfterChanged: true, + validateAfterLoad: true, + }, + } + }, + watch: { + app(value, oldValue) { + if (typeof value !== 'undefined') { + this.updateDetails() + } + }, + }, + mounted() { + this.updateDetails() + }, + methods: { + async updateDetails() { + try { + const { data } = await axios.get(generateUrl('/apps/issuetemplate/details/' + this.app + '/' + this.section)) + this.model = data.model + this.schema = data.schema + } catch (e) { + console.error(e) + this.apps = [] + } + }, + onValidated(isValid, errors) { + this.isValid = isValid + }, + fetchUpdates() { + if (this.isValid) { + return this.model + } else { + return false + } + }, + }, +} +</script> diff --git a/src/components/detailsection.vue b/src/components/detailsection.vue deleted file mode 100644 index af89a4c..0000000 --- a/src/components/detailsection.vue +++ /dev/null @@ -1,85 +0,0 @@ -<!-- - - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> - - - - @author Julius Härtl <jus@bitgrid.net> - - - - @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="details"> - <div class="details-section"> - <vue-form-generator :schema="schema" :model="model" :options="formOptions" @validated="onValidated" /> - </div> - </div> -</template> - -<script> - export default { - props: { - app: String, - section: String, - }, - mounted: function () { - this.updateDetails(); - }, - watch: { - app: function(value, oldValue) { - if (typeof value !== 'undefined') { - this.updateDetails(); - } - } - }, - methods: { - updateDetails: function () { - let self = this; - $.ajax({ - url: OC.generateUrl('/apps/issuetemplate/details/' + this.app + '/' + this.section), - method: 'GET', - success: function (data) { - self.model = data.model; - self.schema = data.schema; - }, - error: function (error) { - console.log(error); - self.apps = []; - } - }); - }, - onValidated(isValid, errors) { - this.isValid = isValid; - }, - fetchUpdates: function() { - if (this.isValid) { - return this.model; - } else { - return false; - } - } - }, - data: function () { - return { - isValid: false, - schema: {}, - model: {}, - formOptions: { - validateAfterChanged: true, - validateAfterLoad: true, - } - } - } - } -</script>
\ No newline at end of file diff --git a/src/main.js b/src/main.js index 48e5202..f88aa1f 100644 --- a/src/main.js +++ b/src/main.js @@ -20,21 +20,21 @@ * */ -import Vue from 'vue'; -import VueFormWizard from 'vue-form-wizard'; -import VueFormGenerator from 'vue-form-generator'; -import VueClipboard from 'vue-clipboard2'; +import Vue from 'vue' +import VueFormWizard from 'vue-form-wizard' +import VueFormGenerator from 'vue-form-generator' +import VueClipboard from 'vue-clipboard2' import App from './App.vue' +// eslint-disable-next-line +__webpack_nonce__ = btoa(OC.requestToken) -/* global __webpack_nonce__ OC */ -__webpack_nonce__ = btoa(OC.requestToken); // eslint-disable-line no-native-reassign Vue.prototype.t = t Vue.prototype.OCA = OCA -Vue.use(VueFormWizard); -Vue.use(VueFormGenerator); -Vue.use(VueClipboard); +Vue.use(VueFormWizard) +Vue.use(VueFormGenerator) +Vue.use(VueClipboard) new Vue({ render: h => h(App), diff --git a/src/webpack.config.js b/src/webpack.config.js index 8e0ecd8..8984c09 100644 --- a/src/webpack.config.js +++ b/src/webpack.config.js @@ -1,56 +1,56 @@ -var path = require('path'); -var webpack = require('webpack'); -const { VueLoaderPlugin } = require('vue-loader'); +const path = require('path') +const webpack = require('webpack') +const { VueLoaderPlugin } = require('vue-loader') module.exports = { entry: './js/init.js', output: { path: path.resolve(__dirname, './build'), publicPath: '/build/', - filename: 'build.js' + filename: 'build.js', }, module: { rules: [ { test: /\.vue$/, - loader: 'vue-loader' + loader: 'vue-loader', }, { test: /\.js$/, loader: 'babel-loader', - exclude: /node_modules/ + exclude: /node_modules/, }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { - name: '[name].[ext]?[hash]' - } - } - ] + name: '[name].[ext]?[hash]', + }, + }, + ], }, plugins: [ - new VueLoaderPlugin() + new VueLoaderPlugin(), ], resolve: { alias: { - 'vue$': 'vue/dist/vue.esm.js' - } + vue$: 'vue/dist/vue.esm.js', + }, }, performance: { - hints: false + hints: false, }, - devtool: '#eval-source-map' -}; + devtool: '#eval-source-map', +} if (process.env.NODE_ENV === 'production') { - module.exports.devtool = '#source-map'; + module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { - NODE_ENV: '"production"' - } - }) - ]); + NODE_ENV: '"production"', + }, + }), + ]) } |