diff options
Diffstat (limited to 'app/assets/javascripts/profile/preferences')
3 files changed, 124 insertions, 23 deletions
diff --git a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue index 8b2006b7c5b..184ee3810ac 100644 --- a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue +++ b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue @@ -1,31 +1,92 @@ <script> -import { s__ } from '~/locale'; +import { GlButton } from '@gitlab/ui'; +import createFlash, { FLASH_TYPES } from '~/flash'; +import { INTEGRATION_VIEW_CONFIGS, i18n } from '../constants'; import IntegrationView from './integration_view.vue'; -const INTEGRATION_VIEW_CONFIGS = { - sourcegraph: { - title: s__('ProfilePreferences|Sourcegraph'), - label: s__('ProfilePreferences|Enable integrated code intelligence on code views'), - formName: 'sourcegraph_enabled', - }, - gitpod: { - title: s__('ProfilePreferences|Gitpod'), - label: s__('ProfilePreferences|Enable Gitpod integration'), - formName: 'gitpod_enabled', - }, -}; +function updateClasses(bodyClasses = '', applicationTheme, layout) { + // Remove body class for any previous theme, re-add current one + document.body.classList.remove(...bodyClasses.split(' ')); + document.body.classList.add(applicationTheme); + + // Toggle container-fluid class + if (layout === 'fluid') { + document + .querySelector('.content-wrapper .container-fluid') + .classList.remove('container-limited'); + } else { + document.querySelector('.content-wrapper .container-fluid').classList.add('container-limited'); + } +} export default { name: 'ProfilePreferences', components: { IntegrationView, + GlButton, }, inject: { integrationViews: { default: [], }, + themes: { + default: [], + }, + userFields: { + default: {}, + }, + formEl: 'formEl', + profilePreferencesPath: 'profilePreferencesPath', + bodyClasses: 'bodyClasses', }, integrationViewConfigs: INTEGRATION_VIEW_CONFIGS, + i18n, + data() { + return { + isSubmitEnabled: true, + }; + }, + computed: { + applicationThemes() { + return this.themes.reduce((themes, theme) => { + const { id, ...rest } = theme; + return { ...themes, [id]: rest }; + }, {}); + }, + }, + created() { + this.formEl.addEventListener('ajax:beforeSend', this.handleLoading); + this.formEl.addEventListener('ajax:success', this.handleSuccess); + this.formEl.addEventListener('ajax:error', this.handleError); + }, + beforeDestroy() { + this.formEl.removeEventListener('ajax:beforeSend', this.handleLoading); + this.formEl.removeEventListener('ajax:success', this.handleSuccess); + this.formEl.removeEventListener('ajax:error', this.handleError); + }, + methods: { + handleLoading() { + this.isSubmitEnabled = false; + }, + handleSuccess(customEvent) { + const formData = new FormData(this.formEl); + updateClasses( + this.bodyClasses, + this.applicationThemes[formData.get('user[theme_id]')].css_class, + this.selectedLayout, + ); + const { message = this.$options.i18n.defaultSuccess, type = FLASH_TYPES.NOTICE } = + customEvent?.detail?.[0] || {}; + createFlash({ message, type }); + this.isSubmitEnabled = true; + }, + handleError(customEvent) { + const { message = this.$options.i18n.defaultError, type = FLASH_TYPES.ALERT } = + customEvent?.detail?.[0] || {}; + createFlash({ message, type }); + this.isSubmitEnabled = true; + }, + }, }; </script> @@ -36,10 +97,10 @@ export default { </div> <div v-if="integrationViews.length" class="col-lg-4 profile-settings-sidebar"> <h4 class="gl-mt-0" data-testid="profile-preferences-integrations-heading"> - {{ s__('ProfilePreferences|Integrations') }} + {{ $options.i18n.integrations }} </h4> <p> - {{ s__('ProfilePreferences|Customize integrations with third party services.') }} + {{ $options.i18n.integrationsDescription }} </p> </div> <div v-if="integrationViews.length" class="col-lg-8"> @@ -52,5 +113,19 @@ export default { :config="$options.integrationViewConfigs[view.name]" /> </div> + <div class="col-lg-4 profile-settings-sidebar"></div> + <div class="col-lg-8"> + <div class="form-group"> + <gl-button + variant="success" + name="commit" + type="submit" + :disabled="!isSubmitEnabled" + :value="$options.i18n.saveChanges" + > + {{ $options.i18n.saveChanges }} + </gl-button> + </div> + </div> </div> </template> diff --git a/app/assets/javascripts/profile/preferences/constants.js b/app/assets/javascripts/profile/preferences/constants.js new file mode 100644 index 00000000000..ea8464ba065 --- /dev/null +++ b/app/assets/javascripts/profile/preferences/constants.js @@ -0,0 +1,22 @@ +import { s__, __ } from '~/locale'; + +export const INTEGRATION_VIEW_CONFIGS = { + sourcegraph: { + title: s__('Preferences|Sourcegraph'), + label: s__('Preferences|Enable integrated code intelligence on code views'), + formName: 'sourcegraph_enabled', + }, + gitpod: { + title: s__('Preferences|Gitpod'), + label: s__('Preferences|Enable Gitpod integration'), + formName: 'gitpod_enabled', + }, +}; + +export const i18n = { + saveChanges: __('Save changes'), + defaultSuccess: __('Preferences saved.'), + defaultError: s__('Preferences|Failed to save preferences.'), + integrations: s__('Preferences|Integrations'), + integrationsDescription: s__('Preferences|Customize integrations with third party services.'), +}; diff --git a/app/assets/javascripts/profile/preferences/profile_preferences_bundle.js b/app/assets/javascripts/profile/preferences/profile_preferences_bundle.js index 744e0174a4e..6520e68d41c 100644 --- a/app/assets/javascripts/profile/preferences/profile_preferences_bundle.js +++ b/app/assets/javascripts/profile/preferences/profile_preferences_bundle.js @@ -3,16 +3,20 @@ import ProfilePreferences from './components/profile_preferences.vue'; export default () => { const el = document.querySelector('#js-profile-preferences-app'); - const shouldParse = ['integrationViews', 'userFields']; + const formEl = document.querySelector('#profile-preferences-form'); + const shouldParse = ['integrationViews', 'themes', 'userFields']; - const provide = Object.keys(el.dataset).reduce((memo, key) => { - let value = el.dataset[key]; - if (shouldParse.includes(key)) { - value = JSON.parse(value); - } + const provide = Object.keys(el.dataset).reduce( + (memo, key) => { + let value = el.dataset[key]; + if (shouldParse.includes(key)) { + value = JSON.parse(value); + } - return { ...memo, [key]: value }; - }, {}); + return { ...memo, [key]: value }; + }, + { formEl }, + ); return new Vue({ el, |