diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-17 13:01:24 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-17 13:01:24 +0100 |
commit | 907dd68e0a18e995dc5772c9bc8117aa150edc25 (patch) | |
tree | f44f01a72ac5b305f7a2fc0dd3c90b992c2db165 /app | |
parent | 4fcff0bfa2f0d8b0a9f60e93bee807334557918f (diff) | |
download | gitlab-ce-907dd68e0a18e995dc5772c9bc8117aa150edc25.tar.gz |
Added move to project in issue inline edit form
[ci skip]
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/issue_show/components/app.vue | 20 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/components/fields/project_move.vue | 84 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/components/form.vue | 14 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/index.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/services/index.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/stores/index.js | 1 | ||||
-rw-r--r-- | app/controllers/projects/issues_controller.rb | 5 | ||||
-rw-r--r-- | app/serializers/issue_entity.rb | 6 | ||||
-rw-r--r-- | app/views/projects/issues/show.html.haml | 2 |
9 files changed, 133 insertions, 7 deletions
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index eb594cfb60b..9d5e69585f1 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -15,6 +15,10 @@ export default { required: true, type: String, }, + canMove: { + required: true, + type: Boolean, + }, canUpdate: { required: true, type: Boolean, @@ -49,6 +53,10 @@ export default { type: String, required: true, }, + projectsAutocompleteUrl: { + type: String, + required: true, + }, }, data() { const store = new Store({ @@ -79,6 +87,7 @@ export default { this.store.formState = { title: this.state.titleText, description: this.state.descriptionText, + move_to_project_id: 0, }; }, closeForm() { @@ -86,7 +95,12 @@ export default { }, updateIssuable() { this.service.updateIssuable(this.store.formState) - .then(() => { + .then(res => res.json()) + .then((data) => { + if (location.pathname !== data.path) { + gl.utils.visitUrl(data.path); + } + eventHub.$emit('close.form'); }) .catch(() => { @@ -153,9 +167,11 @@ export default { <form-component v-if="canUpdate && showForm" :form-state="formState" + :can-move="canMove" :can-destroy="canDestroy" :markdown-docs="markdownDocs" - :markdown-preview-url="markdownPreviewUrl" /> + :markdown-preview-url="markdownPreviewUrl" + :projects-autocomplete-url="projectsAutocompleteUrl" /> <div v-else> <title-component :issuable-ref="issuableRef" diff --git a/app/assets/javascripts/issue_show/components/fields/project_move.vue b/app/assets/javascripts/issue_show/components/fields/project_move.vue new file mode 100644 index 00000000000..701c7f0ea9b --- /dev/null +++ b/app/assets/javascripts/issue_show/components/fields/project_move.vue @@ -0,0 +1,84 @@ +<script> + import tooltipMixin from '../../../vue_shared/mixins/tooltip'; + + export default { + mixins: [ + tooltipMixin, + ], + props: { + formState: { + type: Object, + required: true, + }, + projectsAutocompleteUrl: { + type: String, + required: true, + }, + }, + mounted() { + const $moveDropdown = $(this.$refs['move-dropdown']); + + $moveDropdown.select2({ + ajax: { + url: this.projectsAutocompleteUrl, + quietMillis: 125, + data(term, page, context) { + return { + search: term, + offset_id: context, + }; + }, + results(data) { + const more = data.length >= 50; + const context = data[data.length - 1] ? data[data.length - 1].id : null; + + return { + results: data, + more, + context, + }; + }, + }, + formatResult(project) { + return project.name_with_namespace; + }, + formatSelection(project) { + return project.name_with_namespace; + }, + }) + .on('change', (e) => { + this.formState.move_to_project_id = parseInt(e.target.value, 10); + }); + }, + beforeDestroy() { + $(this.$refs['move-dropdown']).select2('destroy'); + }, + }; +</script> + +<template> + <fieldset> + <label + for="issuable-move" + class="sr-only"> + Move + </label> + <div class="issuable-form-select-holder append-right-5"> + <input + ref="move-dropdown" + type="hidden" + id="issuable-move" + data-placeholder="Move to a different project" /> + </div> + <span + data-placement="auto top" + style="cursor: default" + title="Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location." + ref="tooltip"> + <i + class="fa fa-question-circle" + aria-hidden="true"> + </i> + </span> + </fieldset> +</template> diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue index 7732a1c194a..4cfc95e9888 100644 --- a/app/assets/javascripts/issue_show/components/form.vue +++ b/app/assets/javascripts/issue_show/components/form.vue @@ -2,9 +2,14 @@ import titleField from './fields/title.vue'; import descriptionField from './fields/description.vue'; import editActions from './edit_actions.vue'; + import projectMove from './fields/project_move.vue'; export default { props: { + canMove: { + type: Boolean, + required: true, + }, canDestroy: { type: Boolean, required: true, @@ -21,11 +26,16 @@ type: String, required: true, }, + projectsAutocompleteUrl: { + type: String, + required: true, + }, }, components: { titleField, descriptionField, editActions, + projectMove, }, }; </script> @@ -38,6 +48,10 @@ :form-state="formState" :markdown-preview-url="markdownPreviewUrl" :markdown-docs="markdownDocs" /> + <project-move + v-if="canMove" + :form-state="formState" + :projects-autocomplete-url="projectsAutocompleteUrl" /> <edit-actions :can-destroy="canDestroy" /> </form> diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js index d13e24a468b..666d144f958 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/index.js @@ -23,15 +23,18 @@ document.addEventListener('DOMContentLoaded', () => { const { canUpdate, canDestroy, + canMove, endpoint, issuableRef, markdownPreviewUrl, markdownDocs, + projectsAutocompleteUrl, } = issuableElement.dataset; return { canUpdate: gl.utils.convertPermissionToBoolean(canUpdate), canDestroy: gl.utils.convertPermissionToBoolean(canDestroy), + canMove: gl.utils.convertPermissionToBoolean(canMove), endpoint, issuableRef, initialTitle: issuableTitleElement.innerHTML, @@ -39,6 +42,7 @@ document.addEventListener('DOMContentLoaded', () => { initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '', markdownPreviewUrl, markdownDocs, + projectsAutocompleteUrl, }; }, render(createElement) { @@ -46,6 +50,7 @@ document.addEventListener('DOMContentLoaded', () => { props: { canUpdate: this.canUpdate, canDestroy: this.canDestroy, + canMove: this.canMove, endpoint: this.endpoint, issuableRef: this.issuableRef, initialTitle: this.initialTitle, @@ -53,6 +58,7 @@ document.addEventListener('DOMContentLoaded', () => { initialDescriptionText: this.initialDescriptionText, markdownPreviewUrl: this.markdownPreviewUrl, markdownDocs: this.markdownDocs, + projectsAutocompleteUrl: this.projectsAutocompleteUrl, }, }); }, diff --git a/app/assets/javascripts/issue_show/services/index.js b/app/assets/javascripts/issue_show/services/index.js index 0ceff34cf8b..6f0fd0b1768 100644 --- a/app/assets/javascripts/issue_show/services/index.js +++ b/app/assets/javascripts/issue_show/services/index.js @@ -7,7 +7,7 @@ export default class Service { constructor(endpoint) { this.endpoint = endpoint; - this.resource = Vue.resource(this.endpoint, {}, { + this.resource = Vue.resource(`${this.endpoint}.json`, {}, { realtimeChanges: { method: 'GET', url: `${this.endpoint}/realtime_changes`, diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js index 3232875000d..5717f0a8e74 100644 --- a/app/assets/javascripts/issue_show/stores/index.js +++ b/app/assets/javascripts/issue_show/stores/index.js @@ -15,6 +15,7 @@ export default class Store { this.formState = { title: '', description: '', + move_to_project_id: 0, }; } diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 46438e68d54..9d28a7ed85a 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -148,10 +148,7 @@ class Projects::IssuesController < Projects::ApplicationController format.json do if @issue.valid? - render json: @issue.to_json(methods: [:task_status, :task_status_short], - include: { milestone: {}, - assignees: { only: [:id, :name, :username], methods: [:avatar_url] }, - labels: { methods: :text_color } }) + render json: IssueSerializer.new.represent(@issue) else render json: { errors: @issue.errors.full_messages }, status: :unprocessable_entity end diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index bc4f68710b2..6bc8d0f70c3 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -1,4 +1,6 @@ class IssueEntity < IssuableEntity + include RequestAwareEntity + expose :branch_name expose :confidential expose :assignees, using: API::Entities::UserBasic @@ -7,4 +9,8 @@ class IssueEntity < IssuableEntity expose :project_id expose :milestone, using: API::Entities::Milestone expose :labels, using: LabelEntity + + expose :path do |issue| + namespace_project_issue_path(issue.project.namespace, issue.project, issue) + end end diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 2b095648dcf..a35de038f2f 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -54,9 +54,11 @@ #js-issuable-app{ "data" => { "endpoint" => namespace_project_issue_path(@project.namespace, @project, @issue), "can-update" => can?(current_user, :update_issue, @issue).to_s, "can-destroy" => can?(current_user, :destroy_issue, @issue).to_s, + "can-move" => @issue.can_move?(current_user).to_s, "issuable-ref" => @issue.to_reference, "markdown-preview-url" => preview_markdown_path(@project), "markdown-docs" => help_page_path('user/markdown'), + "projects-autocomplete-url" => autocomplete_projects_path(project_id: @project.id), } } %h2.title= markdown_field(@issue, :title) - if @issue.description.present? |