Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/issuetemplate.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2020-09-03 17:35:02 +0300
committerJulius Härtl <jus@bitgrid.net>2020-09-03 17:35:02 +0300
commit2f63be0a13c4388f7625c87078849abcf10e89d5 (patch)
treea09c3837181b9077f6c5cba968fc31dc9b18c306 /src
parent7419f56fafaf40574ba28f3dc794ad8be99d951e (diff)
Some code refactoring
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'src')
-rw-r--r--src/App.vue390
-rw-r--r--src/components/AppSelector.vue (renamed from src/components/appselector.vue)73
-rw-r--r--src/components/DetailSection.vue92
-rw-r--r--src/components/detailsection.vue85
-rw-r--r--src/main.js18
-rw-r--r--src/webpack.config.js42
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"',
+ },
+ }),
+ ])
}