diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-16 18:08:01 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-16 18:08:01 +0000 |
commit | 8e45d25f7dde6508839ffee719c0ddc2cf6b12d3 (patch) | |
tree | 9839e7fe63b36904d40995ebf519124c9a8f7681 /app/assets/javascripts/releases | |
parent | 00c78fb814d7ce00989ac04edd6cdaa3239da284 (diff) | |
download | gitlab-ce-8e45d25f7dde6508839ffee719c0ddc2cf6b12d3.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/releases')
7 files changed, 320 insertions, 0 deletions
diff --git a/app/assets/javascripts/releases/detail/components/app.vue b/app/assets/javascripts/releases/detail/components/app.vue new file mode 100644 index 00000000000..54a441de886 --- /dev/null +++ b/app/assets/javascripts/releases/detail/components/app.vue @@ -0,0 +1,156 @@ +<script> +import { mapState, mapActions } from 'vuex'; +import { GlButton, GlFormInput, GlFormGroup } from '@gitlab/ui'; +import { __, sprintf } from '~/locale'; +import MarkdownField from '~/vue_shared/components/markdown/field.vue'; +import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; + +export default { + name: 'ReleaseDetailApp', + components: { + GlFormInput, + GlFormGroup, + GlButton, + MarkdownField, + }, + directives: { + autofocusonshow, + }, + computed: { + ...mapState([ + 'isFetchingRelease', + 'fetchError', + 'markdownDocsPath', + 'markdownPreviewPath', + 'releasesPagePath', + ]), + showForm() { + return !this.isFetchingRelease && !this.fetchError; + }, + subtitleText() { + return sprintf( + __( + 'Releases are based on Git tags. We recommend naming tags that fit within semantic versioning, for example %{codeStart}v1.0%{codeEnd}, %{codeStart}v2.0-pre%{codeEnd}.', + ), + { + codeStart: '<code>', + codeEnd: '</code>', + }, + false, + ); + }, + tagName() { + return this.$store.state.release.tagName; + }, + releaseTitle: { + get() { + return this.$store.state.release.name; + }, + set(title) { + this.updateReleaseTitle(title); + }, + }, + releaseNotes: { + get() { + return this.$store.state.release.description; + }, + set(notes) { + this.updateReleaseNotes(notes); + }, + }, + }, + created() { + this.fetchRelease(); + }, + methods: { + ...mapActions([ + 'fetchRelease', + 'updateRelease', + 'updateReleaseTitle', + 'updateReleaseNotes', + 'navigateToReleasesPage', + ]), + }, +}; +</script> +<template> + <div class="d-flex flex-column"> + <p class="pt-3 js-subtitle-text" v-html="subtitleText"></p> + <form v-if="showForm" @submit.prevent="updateRelease()"> + <div class="row"> + <gl-form-group class="col-md-6 col-lg-5 col-xl-4"> + <label for="git-ref">{{ __('Tag name') }}</label> + <gl-form-input + id="git-ref" + v-model="tagName" + type="text" + class="form-control" + aria-describedby="tag-name-help" + disabled + /> + <div id="tag-name-help" class="form-text text-muted"> + {{ __('Choose an existing tag, or create a new one') }} + </div> + </gl-form-group> + </div> + <gl-form-group> + <label for="release-title">{{ __('Release title') }}</label> + <gl-form-input + id="release-title" + ref="releaseTitleInput" + v-model="releaseTitle" + v-autofocusonshow + autofocus + type="text" + class="form-control" + /> + </gl-form-group> + <gl-form-group> + <label for="release-notes">{{ __('Release notes') }}</label> + <div class="bordered-box pr-3 pl-3"> + <markdown-field + :can-attach-file="true" + :markdown-preview-path="markdownPreviewPath" + :markdown-docs-path="markdownDocsPath" + :add-spacing-classes="false" + class="prepend-top-10 append-bottom-10" + > + <textarea + id="release-notes" + slot="textarea" + v-model="releaseNotes" + class="note-textarea js-gfm-input js-autosize markdown-area" + dir="auto" + data-supports-quick-actions="false" + :aria-label="__('Release notes')" + :placeholder="__('Write your release notes or drag your files hereā¦')" + @keydown.meta.enter="updateRelease()" + @keydown.ctrl.enter="updateRelease()" + > + </textarea> + </markdown-field> + </div> + </gl-form-group> + + <div class="d-flex pt-3"> + <gl-button + class="mr-auto js-submit-button" + variant="success" + type="submit" + :aria-label="__('Save changes')" + > + {{ __('Save changes') }} + </gl-button> + <gl-button + class="js-cancel-button" + variant="default" + type="button" + :aria-label="__('Cancel')" + @click="navigateToReleasesPage()" + > + {{ __('Cancel') }} + </gl-button> + </div> + </form> + </div> +</template> diff --git a/app/assets/javascripts/releases/detail/index.js b/app/assets/javascripts/releases/detail/index.js new file mode 100644 index 00000000000..3da971e6d90 --- /dev/null +++ b/app/assets/javascripts/releases/detail/index.js @@ -0,0 +1,19 @@ +import Vue from 'vue'; +import ReleaseDetailApp from './components/app.vue'; +import createStore from './store'; + +export default () => { + const el = document.getElementById('js-edit-release-page'); + + const store = createStore(el.dataset); + store.dispatch('setInitialState', el.dataset); + + return new Vue({ + el, + store, + components: { ReleaseDetailApp }, + render(createElement) { + return createElement('release-detail-app'); + }, + }); +}; diff --git a/app/assets/javascripts/releases/detail/store/actions.js b/app/assets/javascripts/releases/detail/store/actions.js new file mode 100644 index 00000000000..c9749582f5c --- /dev/null +++ b/app/assets/javascripts/releases/detail/store/actions.js @@ -0,0 +1,62 @@ +import * as types from './mutation_types'; +import api from '~/api'; +import createFlash from '~/flash'; +import { s__ } from '~/locale'; +import { redirectTo } from '~/lib/utils/url_utility'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; + +export const setInitialState = ({ commit }, initialState) => + commit(types.SET_INITIAL_STATE, initialState); + +export const requestRelease = ({ commit }) => commit(types.REQUEST_RELEASE); +export const receiveReleaseSuccess = ({ commit }, data) => + commit(types.RECEIVE_RELEASE_SUCCESS, data); +export const receiveReleaseError = ({ commit }, error) => { + commit(types.RECEIVE_RELEASE_ERROR, error); + createFlash(s__('Release|Something went wrong while getting the release details')); +}; + +export const fetchRelease = ({ dispatch, state }) => { + dispatch('requestRelease'); + + return api + .release(state.projectId, state.tagName) + .then(({ data: release }) => { + const camelCasedRelease = convertObjectPropsToCamelCase(release, { deep: true }); + dispatch('receiveReleaseSuccess', camelCasedRelease); + }) + .catch(error => { + dispatch('receiveReleaseError', error); + }); +}; + +export const updateReleaseTitle = ({ commit }, title) => commit(types.UPDATE_RELEASE_TITLE, title); +export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_RELEASE_NOTES, notes); + +export const requestUpdateRelease = ({ commit }) => commit(types.REQUEST_UPDATE_RELEASE); +export const receiveUpdateReleaseSuccess = ({ commit, dispatch }) => { + commit(types.RECEIVE_UPDATE_RELEASE_SUCCESS); + dispatch('navigateToReleasesPage'); +}; +export const receiveUpdateReleaseError = ({ commit }, error) => { + commit(types.RECEIVE_UPDATE_RELEASE_ERROR, error); + createFlash(s__('Release|Something went wrong while saving the release details')); +}; + +export const updateRelease = ({ dispatch, state }) => { + dispatch('requestUpdateRelease'); + + return api + .updateRelease(state.projectId, state.tagName, { + name: state.release.name, + description: state.release.description, + }) + .then(() => dispatch('receiveUpdateReleaseSuccess')) + .catch(error => { + dispatch('receiveUpdateReleaseError', error); + }); +}; + +export const navigateToReleasesPage = ({ state }) => { + redirectTo(state.releasesPagePath); +}; diff --git a/app/assets/javascripts/releases/detail/store/index.js b/app/assets/javascripts/releases/detail/store/index.js new file mode 100644 index 00000000000..e8623a49356 --- /dev/null +++ b/app/assets/javascripts/releases/detail/store/index.js @@ -0,0 +1,14 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export default () => + new Vuex.Store({ + actions, + mutations, + state, + }); diff --git a/app/assets/javascripts/releases/detail/store/mutation_types.js b/app/assets/javascripts/releases/detail/store/mutation_types.js new file mode 100644 index 00000000000..75e1d78a645 --- /dev/null +++ b/app/assets/javascripts/releases/detail/store/mutation_types.js @@ -0,0 +1,12 @@ +export const SET_INITIAL_STATE = 'SET_INITIAL_STATE'; + +export const REQUEST_RELEASE = 'REQUEST_RELEASE'; +export const RECEIVE_RELEASE_SUCCESS = 'RECEIVE_RELEASE_SUCCESS'; +export const RECEIVE_RELEASE_ERROR = 'RECEIVE_RELEASE_ERROR'; + +export const UPDATE_RELEASE_TITLE = 'UPDATE_RELEASE_TITLE'; +export const UPDATE_RELEASE_NOTES = 'UPDATE_RELEASE_NOTES'; + +export const REQUEST_UPDATE_RELEASE = 'REQUEST_UPDATE_RELEASE'; +export const RECEIVE_UPDATE_RELEASE_SUCCESS = 'RECEIVE_UPDATE_RELEASE_SUCCESS'; +export const RECEIVE_UPDATE_RELEASE_ERROR = 'RECEIVE_UPDATE_RELEASE_ERROR'; diff --git a/app/assets/javascripts/releases/detail/store/mutations.js b/app/assets/javascripts/releases/detail/store/mutations.js new file mode 100644 index 00000000000..d739978d755 --- /dev/null +++ b/app/assets/javascripts/releases/detail/store/mutations.js @@ -0,0 +1,42 @@ +import * as types from './mutation_types'; + +export default { + [types.SET_INITIAL_STATE](state, initialState) { + Object.keys(state).forEach(key => { + state[key] = initialState[key]; + }); + }, + + [types.REQUEST_RELEASE](state) { + state.isFetchingRelease = true; + }, + [types.RECEIVE_RELEASE_SUCCESS](state, data) { + state.fetchError = undefined; + state.isFetchingRelease = false; + state.release = data; + }, + [types.RECEIVE_RELEASE_ERROR](state, error) { + state.fetchError = error; + state.isFetchingRelease = false; + state.release = undefined; + }, + + [types.UPDATE_RELEASE_TITLE](state, title) { + state.release.name = title; + }, + [types.UPDATE_RELEASE_NOTES](state, notes) { + state.release.description = notes; + }, + + [types.REQUEST_UPDATE_RELEASE](state) { + state.isUpdatingRelease = true; + }, + [types.RECEIVE_UPDATE_RELEASE_SUCCESS](state) { + state.updateError = undefined; + state.isUpdatingRelease = false; + }, + [types.RECEIVE_UPDATE_RELEASE_ERROR](state, error) { + state.updateError = error; + state.isUpdatingRelease = false; + }, +}; diff --git a/app/assets/javascripts/releases/detail/store/state.js b/app/assets/javascripts/releases/detail/store/state.js new file mode 100644 index 00000000000..ff98e2bed78 --- /dev/null +++ b/app/assets/javascripts/releases/detail/store/state.js @@ -0,0 +1,15 @@ +export default () => ({ + projectId: null, + tagName: null, + releasesPagePath: null, + markdownDocsPath: null, + markdownPreviewPath: null, + + release: null, + + isFetchingRelease: false, + fetchError: null, + + isUpdatingRelease: false, + updateError: null, +}); |