summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/snippets/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/snippets/components')
-rw-r--r--app/assets/javascripts/snippets/components/edit.vue211
-rw-r--r--app/assets/javascripts/snippets/components/snippet_description_edit.vue2
-rw-r--r--app/assets/javascripts/snippets/components/snippet_header.vue14
3 files changed, 219 insertions, 8 deletions
diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue
new file mode 100644
index 00000000000..2185b1d67e4
--- /dev/null
+++ b/app/assets/javascripts/snippets/components/edit.vue
@@ -0,0 +1,211 @@
+<script>
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+
+import Flash from '~/flash';
+import { __, sprintf } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+import TitleField from '~/vue_shared/components/form/title.vue';
+import { getBaseURL, joinPaths, redirectTo } from '~/lib/utils/url_utility';
+import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
+
+import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql';
+import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql';
+import { getSnippetMixin } from '../mixins/snippets';
+import { SNIPPET_VISIBILITY_PRIVATE } from '../constants';
+import SnippetBlobEdit from './snippet_blob_edit.vue';
+import SnippetVisibilityEdit from './snippet_visibility_edit.vue';
+import SnippetDescriptionEdit from './snippet_description_edit.vue';
+
+export default {
+ components: {
+ SnippetDescriptionEdit,
+ SnippetVisibilityEdit,
+ SnippetBlobEdit,
+ TitleField,
+ FormFooterActions,
+ GlButton,
+ GlLoadingIcon,
+ },
+ mixins: [getSnippetMixin],
+ props: {
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ markdownDocsPath: {
+ type: String,
+ required: true,
+ },
+ visibilityHelpLink: {
+ type: String,
+ default: '',
+ required: false,
+ },
+ projectPath: {
+ type: String,
+ default: '',
+ required: false,
+ },
+ },
+ data() {
+ return {
+ blob: {},
+ fileName: '',
+ content: '',
+ isContentLoading: true,
+ isUpdating: false,
+ newSnippet: false,
+ };
+ },
+ computed: {
+ updatePrevented() {
+ return this.snippet.title === '' || this.content === '' || this.isUpdating;
+ },
+ isProjectSnippet() {
+ return Boolean(this.projectPath);
+ },
+ apiData() {
+ return {
+ id: this.snippet.id,
+ title: this.snippet.title,
+ description: this.snippet.description,
+ visibilityLevel: this.snippet.visibilityLevel,
+ fileName: this.fileName,
+ content: this.content,
+ };
+ },
+ saveButtonLabel() {
+ if (this.newSnippet) {
+ return __('Create snippet');
+ }
+ return this.isUpdating ? __('Saving') : __('Save changes');
+ },
+ cancelButtonHref() {
+ return this.projectPath ? `/${this.projectPath}/snippets` : `/snippets`;
+ },
+ titleFieldId() {
+ return `${this.isProjectSnippet ? 'project' : 'personal'}_snippet_title`;
+ },
+ descriptionFieldId() {
+ return `${this.isProjectSnippet ? 'project' : 'personal'}_snippet_description`;
+ },
+ },
+ methods: {
+ updateFileName(newName) {
+ this.fileName = newName;
+ },
+ flashAPIFailure(err) {
+ Flash(sprintf(__("Can't update snippet: %{err}"), { err }));
+ },
+ onNewSnippetFetched() {
+ this.newSnippet = true;
+ this.snippet = this.$options.newSnippetSchema;
+ this.blob = this.snippet.blob;
+ this.isContentLoading = false;
+ },
+ onExistingSnippetFetched() {
+ this.newSnippet = false;
+ const { blob } = this.snippet;
+ this.blob = blob;
+ this.fileName = blob.name;
+ const baseUrl = getBaseURL();
+ const url = joinPaths(baseUrl, blob.rawPath);
+
+ axios
+ .get(url)
+ .then(res => {
+ this.content = res.data;
+ this.isContentLoading = false;
+ })
+ .catch(e => this.flashAPIFailure(e));
+ },
+ onSnippetFetch(snippetRes) {
+ if (snippetRes.data.snippets.edges.length === 0) {
+ this.onNewSnippetFetched();
+ } else {
+ this.onExistingSnippetFetched();
+ }
+ },
+ handleFormSubmit() {
+ this.isUpdating = true;
+ this.$apollo
+ .mutate({
+ mutation: this.newSnippet ? CreateSnippetMutation : UpdateSnippetMutation,
+ variables: {
+ input: {
+ ...this.apiData,
+ projectPath: this.newSnippet ? this.projectPath : undefined,
+ },
+ },
+ })
+ .then(({ data }) => {
+ const baseObj = this.newSnippet ? data?.createSnippet : data?.updateSnippet;
+
+ const errors = baseObj?.errors;
+ if (errors.length) {
+ this.flashAPIFailure(errors[0]);
+ }
+ redirectTo(baseObj.snippet.webUrl);
+ })
+ .catch(e => {
+ this.isUpdating = false;
+ this.flashAPIFailure(e);
+ });
+ },
+ },
+ newSnippetSchema: {
+ title: '',
+ description: '',
+ visibilityLevel: SNIPPET_VISIBILITY_PRIVATE,
+ blob: {},
+ },
+};
+</script>
+<template>
+ <form
+ class="snippet-form js-requires-input js-quick-submit common-note-form"
+ :data-snippet-type="isProjectSnippet ? 'project' : 'personal'"
+ >
+ <gl-loading-icon
+ v-if="isLoading"
+ :label="__('Loading snippet')"
+ size="lg"
+ class="loading-animation prepend-top-20 append-bottom-20"
+ />
+ <template v-else>
+ <title-field :id="titleFieldId" v-model="snippet.title" required :autofocus="true" />
+ <snippet-description-edit
+ :id="descriptionFieldId"
+ v-model="snippet.description"
+ :markdown-preview-path="markdownPreviewPath"
+ :markdown-docs-path="markdownDocsPath"
+ />
+ <snippet-blob-edit
+ v-model="content"
+ :file-name="fileName"
+ :is-loading="isContentLoading"
+ @name-change="updateFileName"
+ />
+ <snippet-visibility-edit
+ v-model="snippet.visibilityLevel"
+ :help-link="visibilityHelpLink"
+ :is-project-snippet="isProjectSnippet"
+ />
+ <form-footer-actions>
+ <template #prepend>
+ <gl-button
+ type="submit"
+ category="primary"
+ variant="success"
+ :disabled="updatePrevented"
+ @click="handleFormSubmit"
+ >{{ saveButtonLabel }}</gl-button
+ >
+ </template>
+ <template #append>
+ <gl-button :href="cancelButtonHref">{{ __('Cancel') }}</gl-button>
+ </template>
+ </form-footer-actions>
+ </template>
+ </form>
+</template>
diff --git a/app/assets/javascripts/snippets/components/snippet_description_edit.vue b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
index 68810f8ab3f..6f3a86be8d7 100644
--- a/app/assets/javascripts/snippets/components/snippet_description_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
@@ -50,7 +50,6 @@ export default {
:markdown-docs-path="markdownDocsPath"
>
<textarea
- id="snippet-description"
slot="textarea"
class="note-textarea js-gfm-input js-autosize markdown-area
qa-description-textarea"
@@ -59,6 +58,7 @@ export default {
:value="value"
:aria-label="__('Description')"
:placeholder="__('Write a comment or drag your files hereā€¦')"
+ v-bind="$attrs"
@input="$emit('input', $event.target.value)"
>
</textarea>
diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue
index 79b191cb25a..30a23b51bc4 100644
--- a/app/assets/javascripts/snippets/components/snippet_header.vue
+++ b/app/assets/javascripts/snippets/components/snippet_header.vue
@@ -9,7 +9,7 @@ import {
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
- GlNewButton,
+ GlButton,
} from '@gitlab/ui';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -28,7 +28,7 @@ export default {
GlDropdown,
GlDropdownItem,
TimeAgoTooltip,
- GlNewButton,
+ GlButton,
},
apollo: {
canCreateSnippet: {
@@ -186,7 +186,7 @@ export default {
<div class="detail-page-header-actions">
<div class="d-none d-sm-flex">
<template v-for="(action, index) in personalSnippetActions">
- <gl-new-button
+ <gl-button
v-if="action.condition"
:key="index"
:disabled="action.disabled"
@@ -197,7 +197,7 @@ export default {
@click="action.click ? action.click() : undefined"
>
{{ action.text }}
- </gl-new-button>
+ </gl-button>
</template>
</div>
<div class="d-block d-sm-none dropdown">
@@ -227,8 +227,8 @@ export default {
</gl-sprintf>
<template #modal-footer>
- <gl-new-button @click="closeDeleteModal">{{ __('Cancel') }}</gl-new-button>
- <gl-new-button
+ <gl-button @click="closeDeleteModal">{{ __('Cancel') }}</gl-button>
+ <gl-button
variant="danger"
category="primary"
:disabled="isDeleting"
@@ -237,7 +237,7 @@ export default {
>
<gl-loading-icon v-if="isDeleting" inline />
{{ __('Delete snippet') }}
- </gl-new-button>
+ </gl-button>
</template>
</gl-modal>
</div>