debian-mirror-gitlab/app/assets/javascripts/boards/components/sidebar/board_sidebar_title.vue

169 lines
4.7 KiB
Vue

<script>
import { GlAlert, GlButton, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
export default {
components: {
GlForm,
GlAlert,
GlButton,
GlFormGroup,
GlFormInput,
BoardEditableItem,
},
directives: {
autofocusonshow,
},
data() {
return {
title: '',
loading: false,
showChangesAlert: false,
};
},
computed: {
...mapGetters({ item: 'activeBoardItem' }),
pendingChangesStorageKey() {
return this.getPendingChangesKey(this.item);
},
projectPath() {
const referencePath = this.item.referencePath || '';
return referencePath.slice(0, referencePath.indexOf('#'));
},
validationState() {
return Boolean(this.title);
},
},
watch: {
item: {
handler(updatedItem, formerItem) {
if (formerItem?.title !== this.title) {
localStorage.setItem(this.getPendingChangesKey(formerItem), this.title);
}
this.title = updatedItem.title;
this.setPendingState();
},
immediate: true,
},
},
methods: {
...mapActions(['setActiveItemTitle', 'setError']),
getPendingChangesKey(item) {
if (!item) {
return '';
}
return joinPaths(
window.location.pathname.slice(1),
String(item.id),
'item-title-pending-changes',
);
},
async setPendingState() {
const pendingChanges = localStorage.getItem(this.pendingChangesStorageKey);
if (pendingChanges) {
this.title = pendingChanges;
this.showChangesAlert = true;
await this.$nextTick();
this.$refs.sidebarItem.expand();
} else {
this.showChangesAlert = false;
}
},
cancel() {
this.title = this.item.title;
this.$refs.sidebarItem.collapse();
this.showChangesAlert = false;
localStorage.removeItem(this.pendingChangesStorageKey);
},
async setTitle() {
this.$refs.sidebarItem.collapse();
if (!this.title || this.title === this.item.title) {
return;
}
try {
this.loading = true;
await this.setActiveItemTitle({ title: this.title, projectPath: this.projectPath });
localStorage.removeItem(this.pendingChangesStorageKey);
this.showChangesAlert = false;
} catch (e) {
this.title = this.item.title;
this.setError({ error: e, message: this.$options.i18n.updateTitleError });
} finally {
this.loading = false;
}
},
handleOffClick() {
if (this.title !== this.item.title) {
this.showChangesAlert = true;
localStorage.setItem(this.pendingChangesStorageKey, this.title);
} else {
this.$refs.sidebarItem.collapse();
}
},
},
i18n: {
titlePlaceholder: __('Title'),
submitButton: __('Save changes'),
cancelButton: __('Cancel'),
updateTitleError: __('An error occurred when updating the title'),
invalidFeedback: __('A title is required'),
reviewYourChanges: __('Changes to the title have not been saved'),
},
};
</script>
<template>
<board-editable-item
ref="sidebarItem"
toggle-header
:loading="loading"
:handle-off-click="false"
@off-click="handleOffClick"
>
<template #title>
<span class="gl-font-weight-bold" data-testid="item-title">{{ item.title }}</span>
</template>
<template #collapsed>
<span class="gl-text-gray-800">{{ item.referencePath }}</span>
</template>
<gl-alert v-if="showChangesAlert" variant="warning" class="gl-mb-5" :dismissible="false">
{{ $options.i18n.reviewYourChanges }}
</gl-alert>
<gl-form @submit.prevent="setTitle">
<gl-form-group :invalid-feedback="$options.i18n.invalidFeedback" :state="validationState">
<gl-form-input
v-model="title"
v-autofocusonshow
:placeholder="$options.i18n.titlePlaceholder"
:state="validationState"
/>
</gl-form-group>
<div class="gl-display-flex gl-w-full gl-justify-content-space-between gl-mt-5">
<gl-button
variant="confirm"
size="small"
data-testid="submit-button"
:disabled="!title"
@click="setTitle"
>
{{ $options.i18n.submitButton }}
</gl-button>
<gl-button size="small" data-testid="cancel-button" @click="cancel">
{{ $options.i18n.cancelButton }}
</gl-button>
</div>
</gl-form>
</board-editable-item>
</template>