Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 04:45:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 04:45:44 +0300
commit85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch)
tree9160f299afd8c80c038f08e1545be119f5e3f1e1 /app/assets/javascripts/integrations
parent15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff)
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'app/assets/javascripts/integrations')
-rw-r--r--app/assets/javascripts/integrations/edit/components/active_checkbox.vue (renamed from app/assets/javascripts/integrations/edit/components/active_toggle.vue)30
-rw-r--r--app/assets/javascripts/integrations/edit/components/dynamic_field.vue1
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue59
-rw-r--r--app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue7
-rw-r--r--app/assets/javascripts/integrations/edit/components/override_dropdown.vue39
-rw-r--r--app/assets/javascripts/integrations/edit/constants.js17
-rw-r--r--app/assets/javascripts/integrations/edit/index.js26
-rw-r--r--app/assets/javascripts/integrations/edit/store/actions.js3
-rw-r--r--app/assets/javascripts/integrations/edit/store/getters.js6
-rw-r--r--app/assets/javascripts/integrations/edit/store/index.js1
-rw-r--r--app/assets/javascripts/integrations/edit/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/integrations/edit/store/mutations.js6
-rw-r--r--app/assets/javascripts/integrations/edit/store/state.js8
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js116
14 files changed, 189 insertions, 133 deletions
diff --git a/app/assets/javascripts/integrations/edit/components/active_toggle.vue b/app/assets/javascripts/integrations/edit/components/active_checkbox.vue
index e6a96600539..6698984d02f 100644
--- a/app/assets/javascripts/integrations/edit/components/active_toggle.vue
+++ b/app/assets/javascripts/integrations/edit/components/active_checkbox.vue
@@ -1,36 +1,31 @@
<script>
import { mapGetters } from 'vuex';
-import { GlFormGroup, GlToggle } from '@gitlab/ui';
+import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
import eventHub from '../event_hub';
export default {
- name: 'ActiveToggle',
+ name: 'ActiveCheckbox',
components: {
GlFormGroup,
- GlToggle,
- },
- props: {
- initialActivated: {
- type: Boolean,
- required: true,
- },
+ GlFormCheckbox,
},
data() {
return {
- activated: this.initialActivated,
+ activated: false,
};
},
computed: {
- ...mapGetters(['isInheriting']),
+ ...mapGetters(['isInheriting', 'propsSource']),
},
mounted() {
+ this.activated = this.propsSource.initialActivated;
// Initialize view
this.$nextTick(() => {
- this.onToggle(this.activated);
+ this.onChange(this.activated);
});
},
methods: {
- onToggle(e) {
+ onChange(e) {
eventHub.$emit('toggle', e);
},
},
@@ -39,12 +34,15 @@ export default {
<template>
<gl-form-group :label="__('Enable integration')" label-for="service[active]">
- <gl-toggle
+ <input name="service[active]" type="hidden" :value="activated || false" />
+ <gl-form-checkbox
v-model="activated"
name="service[active]"
class="gl-display-block gl-line-height-0"
:disabled="isInheriting"
- @change="onToggle"
- />
+ @change="onChange"
+ >
+ {{ __('Active') }}
+ </gl-form-checkbox>
</gl-form-group>
</template>
diff --git a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue
index 090381b8da4..9dde1ed1055 100644
--- a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue
+++ b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue
@@ -1,4 +1,5 @@
<script>
+/* eslint-disable vue/no-v-html */
import { mapGetters } from 'vuex';
import { capitalize, lowerCase, isEmpty } from 'lodash';
import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui';
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index 5088664c3bd..0460ed6791e 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -1,9 +1,11 @@
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
+import { GlButton } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import eventHub from '../event_hub';
import OverrideDropdown from './override_dropdown.vue';
-import ActiveToggle from './active_toggle.vue';
+import ActiveCheckbox from './active_checkbox.vue';
import JiraTriggerFields from './jira_trigger_fields.vue';
import JiraIssuesFields from './jira_issues_fields.vue';
import TriggerFields from './trigger_fields.vue';
@@ -13,16 +15,20 @@ export default {
name: 'IntegrationForm',
components: {
OverrideDropdown,
- ActiveToggle,
+ ActiveCheckbox,
JiraTriggerFields,
JiraIssuesFields,
TriggerFields,
DynamicField,
+ GlButton,
},
mixins: [glFeatureFlagsMixin()],
computed: {
- ...mapGetters(['currentKey', 'propsSource']),
- ...mapState(['adminState', 'override']),
+ ...mapGetters(['currentKey', 'propsSource', 'isSavingOrTesting']),
+ ...mapState(['defaultState', 'override', 'isSaving', 'isTesting']),
+ isEditable() {
+ return this.propsSource.editable;
+ },
isJira() {
return this.propsSource.type === 'jira';
},
@@ -31,7 +37,15 @@ export default {
},
},
methods: {
- ...mapActions(['setOverride']),
+ ...mapActions(['setOverride', 'setIsSaving', 'setIsTesting']),
+ onSaveClick() {
+ this.setIsSaving(true);
+ eventHub.$emit('saveIntegration');
+ },
+ onTestClick() {
+ this.setIsTesting(true);
+ eventHub.$emit('testIntegration');
+ },
},
};
</script>
@@ -39,16 +53,13 @@ export default {
<template>
<div>
<override-dropdown
- v-if="adminState !== null"
- :inherit-from-id="adminState.id"
+ v-if="defaultState !== null"
+ :inherit-from-id="defaultState.id"
:override="override"
+ :learn-more-path="propsSource.learnMorePath"
@change="setOverride"
/>
- <active-toggle
- v-if="propsSource.showActive"
- :key="`${currentKey}-active-toggle`"
- v-bind="propsSource.activeToggleProps"
- />
+ <active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" />
<jira-trigger-fields
v-if="isJira"
:key="`${currentKey}-jira-trigger-fields`"
@@ -70,5 +81,29 @@ export default {
:key="`${currentKey}-jira-issues-fields`"
v-bind="propsSource.jiraIssuesProps"
/>
+ <div v-if="isEditable" class="footer-block row-content-block">
+ <gl-button
+ category="primary"
+ variant="success"
+ type="submit"
+ :loading="isSaving"
+ :disabled="isSavingOrTesting"
+ data-qa-selector="save_changes_button"
+ @click.prevent="onSaveClick"
+ >
+ {{ __('Save changes') }}
+ </gl-button>
+ <gl-button
+ v-if="propsSource.canTest"
+ :loading="isTesting"
+ :disabled="isSavingOrTesting"
+ :href="propsSource.testPath"
+ @click.prevent="onTestClick"
+ >
+ {{ __('Test settings') }}
+ </gl-button>
+
+ <gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue
index 5a1f86718b0..1baa2b440b0 100644
--- a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue
+++ b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue
@@ -37,6 +37,11 @@ export default {
required: false,
default: null,
},
+ gitlabIssuesEnabled: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
upgradePlanPath: {
type: String,
required: false,
@@ -133,7 +138,7 @@ export default {
:disabled="!enableJiraIssues"
/>
</gl-form-group>
- <p>
+ <p v-if="gitlabIssuesEnabled">
<gl-sprintf
:message="
s__(
diff --git a/app/assets/javascripts/integrations/edit/components/override_dropdown.vue b/app/assets/javascripts/integrations/edit/components/override_dropdown.vue
index accfc26974c..c31dada8d2f 100644
--- a/app/assets/javascripts/integrations/edit/components/override_dropdown.vue
+++ b/app/assets/javascripts/integrations/edit/components/override_dropdown.vue
@@ -1,6 +1,8 @@
<script>
-import { GlNewDropdown, GlNewDropdownItem } from '@gitlab/ui';
+import { mapState } from 'vuex';
+import { GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
+import { defaultIntegrationLevel, overrideDropdownDescriptions } from '../constants';
const dropdownOptions = [
{
@@ -17,14 +19,20 @@ export default {
dropdownOptions,
name: 'OverrideDropdown',
components: {
- GlNewDropdown,
- GlNewDropdownItem,
+ GlDropdown,
+ GlDropdownItem,
+ GlLink,
},
props: {
inheritFromId: {
type: Number,
required: true,
},
+ learnMorePath: {
+ type: String,
+ required: false,
+ default: null,
+ },
override: {
type: Boolean,
required: true,
@@ -35,6 +43,16 @@ export default {
selected: dropdownOptions.find(x => x.value === this.override),
};
},
+ computed: {
+ ...mapState(['defaultState']),
+ description() {
+ const level = this.defaultState.integrationLevel;
+
+ return (
+ overrideDropdownDescriptions[level] || overrideDropdownDescriptions[defaultIntegrationLevel]
+ );
+ },
+ },
methods: {
onClick(option) {
this.selected = option;
@@ -48,16 +66,21 @@ export default {
<div
class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline gl-py-4 gl-mt-5 gl-mb-6 gl-border-t-1 gl-border-t-solid gl-border-b-1 gl-border-b-solid gl-border-gray-100"
>
- <span>{{ s__('Integrations|Default settings are inherited from the instance level.') }}</span>
+ <span
+ >{{ description }}
+ <gl-link v-if="learnMorePath" :href="learnMorePath" target="_blank">{{
+ __('Learn more')
+ }}</gl-link>
+ </span>
<input name="service[inherit_from_id]" :value="override ? '' : inheritFromId" type="hidden" />
- <gl-new-dropdown :text="selected.text">
- <gl-new-dropdown-item
+ <gl-dropdown :text="selected.text">
+ <gl-dropdown-item
v-for="option in $options.dropdownOptions"
:key="option.value"
@click="onClick(option)"
>
{{ option.text }}
- </gl-new-dropdown-item>
- </gl-new-dropdown>
+ </gl-dropdown-item>
+ </gl-dropdown>
</div>
</template>
diff --git a/app/assets/javascripts/integrations/edit/constants.js b/app/assets/javascripts/integrations/edit/constants.js
new file mode 100644
index 00000000000..b74ae209eb7
--- /dev/null
+++ b/app/assets/javascripts/integrations/edit/constants.js
@@ -0,0 +1,17 @@
+import { s__ } from '~/locale';
+
+export const integrationLevels = {
+ GROUP: 'group',
+ INSTANCE: 'instance',
+};
+
+export const defaultIntegrationLevel = integrationLevels.INSTANCE;
+
+export const overrideDropdownDescriptions = {
+ [integrationLevels.GROUP]: s__(
+ 'Integrations|Default settings are inherited from the group level.',
+ ),
+ [integrationLevels.INSTANCE]: s__(
+ 'Integrations|Default settings are inherited from the instance level.',
+ ),
+};
diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js
index ea5463832ce..248ee62d43a 100644
--- a/app/assets/javascripts/integrations/edit/index.js
+++ b/app/assets/javascripts/integrations/edit/index.js
@@ -19,27 +19,36 @@ function parseDatasetToProps(data) {
projectKey,
upgradePlanPath,
editProjectPath,
+ learnMorePath,
triggerEvents,
fields,
inheritFromId,
+ integrationLevel,
+ cancelPath,
+ testPath,
...booleanAttributes
} = data;
const {
showActive,
activated,
+ editable,
+ canTest,
commitEvents,
mergeRequestEvents,
enableComments,
showJiraIssuesIntegration,
enableJiraIssues,
+ gitlabIssuesEnabled,
} = parseBooleanInData(booleanAttributes);
return {
- activeToggleProps: {
- initialActivated: activated,
- },
+ initialActivated: activated,
showActive,
type,
+ cancelPath,
+ editable,
+ canTest,
+ testPath,
triggerFieldsProps: {
initialTriggerCommit: commitEvents,
initialTriggerMergeRequest: mergeRequestEvents,
@@ -50,17 +59,20 @@ function parseDatasetToProps(data) {
showJiraIssuesIntegration,
initialEnableJiraIssues: enableJiraIssues,
initialProjectKey: projectKey,
+ gitlabIssuesEnabled,
upgradePlanPath,
editProjectPath,
},
+ learnMorePath,
triggerEvents: JSON.parse(triggerEvents),
fields: JSON.parse(fields),
inheritFromId: parseInt(inheritFromId, 10),
+ integrationLevel,
id: parseInt(id, 10),
};
}
-export default (el, adminEl) => {
+export default (el, defaultEl) => {
if (!el) {
return null;
}
@@ -68,12 +80,12 @@ export default (el, adminEl) => {
const props = parseDatasetToProps(el.dataset);
const initialState = {
- adminState: null,
+ defaultState: null,
customState: props,
};
- if (adminEl) {
- initialState.adminState = Object.freeze(parseDatasetToProps(adminEl.dataset));
+ if (defaultEl) {
+ initialState.defaultState = Object.freeze(parseDatasetToProps(defaultEl.dataset));
}
return new Vue({
diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js
index 3decdaab55d..199c9074ead 100644
--- a/app/assets/javascripts/integrations/edit/store/actions.js
+++ b/app/assets/javascripts/integrations/edit/store/actions.js
@@ -1,4 +1,5 @@
import * as types from './mutation_types';
-// eslint-disable-next-line import/prefer-default-export
export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override);
+export const setIsSaving = ({ commit }, isSaving) => commit(types.SET_IS_SAVING, isSaving);
+export const setIsTesting = ({ commit }, isTesting) => commit(types.SET_IS_TESTING, isTesting);
diff --git a/app/assets/javascripts/integrations/edit/store/getters.js b/app/assets/javascripts/integrations/edit/store/getters.js
index b68bd668980..4ee5f11855c 100644
--- a/app/assets/javascripts/integrations/edit/store/getters.js
+++ b/app/assets/javascripts/integrations/edit/store/getters.js
@@ -1,6 +1,8 @@
-export const isInheriting = state => (state.adminState === null ? false : !state.override);
+export const isInheriting = state => (state.defaultState === null ? false : !state.override);
+
+export const isSavingOrTesting = state => state.isSaving || state.isTesting;
export const propsSource = (state, getters) =>
- getters.isInheriting ? state.adminState : state.customState;
+ getters.isInheriting ? state.defaultState : state.customState;
export const currentKey = (state, getters) => (getters.isInheriting ? 'admin' : 'custom');
diff --git a/app/assets/javascripts/integrations/edit/store/index.js b/app/assets/javascripts/integrations/edit/store/index.js
index eea5e48780d..a8375f345c6 100644
--- a/app/assets/javascripts/integrations/edit/store/index.js
+++ b/app/assets/javascripts/integrations/edit/store/index.js
@@ -7,7 +7,6 @@ import createState from './state';
Vue.use(Vuex);
-// eslint-disable-next-line import/prefer-default-export
export const createStore = (initialState = {}) =>
new Vuex.Store({
actions,
diff --git a/app/assets/javascripts/integrations/edit/store/mutation_types.js b/app/assets/javascripts/integrations/edit/store/mutation_types.js
index 274afe3fb49..0dae8ea079e 100644
--- a/app/assets/javascripts/integrations/edit/store/mutation_types.js
+++ b/app/assets/javascripts/integrations/edit/store/mutation_types.js
@@ -1,2 +1,3 @@
-// eslint-disable-next-line import/prefer-default-export
export const SET_OVERRIDE = 'SET_OVERRIDE';
+export const SET_IS_SAVING = 'SET_IS_SAVING';
+export const SET_IS_TESTING = 'SET_IS_TESTING';
diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js
index 8757d415197..8ac3c476f9e 100644
--- a/app/assets/javascripts/integrations/edit/store/mutations.js
+++ b/app/assets/javascripts/integrations/edit/store/mutations.js
@@ -4,4 +4,10 @@ export default {
[types.SET_OVERRIDE](state, override) {
state.override = override;
},
+ [types.SET_IS_SAVING](state, isSaving) {
+ state.isSaving = isSaving;
+ },
+ [types.SET_IS_TESTING](state, isTesting) {
+ state.isTesting = isTesting;
+ },
};
diff --git a/app/assets/javascripts/integrations/edit/store/state.js b/app/assets/javascripts/integrations/edit/store/state.js
index 95c1a2be500..a9ecee6c539 100644
--- a/app/assets/javascripts/integrations/edit/store/state.js
+++ b/app/assets/javascripts/integrations/edit/store/state.js
@@ -1,9 +1,11 @@
-export default ({ adminState = null, customState = {} } = {}) => {
- const override = adminState !== null ? adminState.id !== customState.inheritFromId : false;
+export default ({ defaultState = null, customState = {} } = {}) => {
+ const override = defaultState !== null ? defaultState.id !== customState.inheritFromId : false;
return {
override,
- adminState,
+ defaultState,
customState,
+ isSaving: false,
+ isTesting: false,
};
};
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index 1135065b06c..1d0814125e6 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import axios from '../lib/utils/axios_utils';
-import { deprecatedCreateFlash as flash } from '../flash';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
+import toast from '~/vue_shared/plugins/global_toast';
import initForm from './edit';
import eventHub from './edit/event_hub';
@@ -10,65 +10,63 @@ export default class IntegrationSettingsForm {
this.$form = $(formSelector);
this.formActive = false;
+ this.vue = null;
+
// Form Metadata
- this.canTestService = this.$form.data('canTest');
this.testEndPoint = this.$form.data('testUrl');
-
- // Form Child Elements
- this.$submitBtn = this.$form.find('button[type="submit"]');
- this.$submitBtnLoader = this.$submitBtn.find('.js-btn-spinner');
- this.$submitBtnLabel = this.$submitBtn.find('.js-btn-label');
}
init() {
// Init Vue component
- initForm(
+ this.vue = initForm(
document.querySelector('.js-vue-integration-settings'),
- document.querySelector('.js-vue-admin-integration-settings'),
+ document.querySelector('.js-vue-default-integration-settings'),
);
eventHub.$on('toggle', active => {
this.formActive = active;
- this.handleServiceToggle();
+ this.toggleServiceState();
+ });
+ eventHub.$on('testIntegration', () => {
+ this.testIntegration();
+ });
+ eventHub.$on('saveIntegration', () => {
+ this.saveIntegration();
});
-
- // Bind Event Listeners
- this.$submitBtn.on('click', e => this.handleSettingsSave(e));
}
- handleSettingsSave(e) {
- // Check if Service is marked active, as if not marked active,
- // We can skip testing it and directly go ahead to allow form to
- // be submitted
- if (!this.formActive) {
- return;
+ saveIntegration() {
+ // Service was marked active so now we check;
+ // 1) If form contents are valid
+ // 2) If this service can be saved
+ // If both conditions are true, we override form submission
+ // and save the service using provided configuration.
+ if (this.$form.get(0).checkValidity()) {
+ this.$form.submit();
+ } else {
+ eventHub.$emit('validateForm');
+ this.vue.$store.dispatch('setIsSaving', false);
}
+ }
+ testIntegration() {
// Service was marked active so now we check;
// 1) If form contents are valid
// 2) If this service can be tested
// If both conditions are true, we override form submission
// and test the service using provided configuration.
if (this.$form.get(0).checkValidity()) {
- if (this.canTestService) {
- e.preventDefault();
- // eslint-disable-next-line no-jquery/no-serialize
- this.testSettings(this.$form.serialize());
- }
+ // eslint-disable-next-line no-jquery/no-serialize
+ this.testSettings(this.$form.serialize());
} else {
- e.preventDefault();
eventHub.$emit('validateForm');
+ this.vue.$store.dispatch('setIsTesting', false);
}
}
- handleServiceToggle() {
- this.toggleServiceState();
- }
-
/**
* Change Form's validation enforcement based on service status (active/inactive)
*/
toggleServiceState() {
- this.toggleSubmitBtnLabel();
if (this.formActive) {
this.$form.removeAttr('novalidate');
} else if (!this.$form.attr('novalidate')) {
@@ -77,67 +75,23 @@ export default class IntegrationSettingsForm {
}
/**
- * Toggle Submit button label based on Integration status and ability to test service
- */
- toggleSubmitBtnLabel() {
- let btnLabel = __('Save changes');
-
- if (this.formActive && this.canTestService) {
- btnLabel = __('Test settings and save changes');
- }
-
- this.$submitBtnLabel.text(btnLabel);
- }
-
- /**
- * Toggle Submit button state based on provided boolean value of `saveTestActive`
- * When enabled, it does two things, and reverts back when disabled
- *
- * 1. It shows load spinner on submit button
- * 2. Makes submit button disabled
- */
- toggleSubmitBtnState(saveTestActive) {
- if (saveTestActive) {
- this.$submitBtn.disable();
- this.$submitBtnLoader.removeClass('hidden');
- } else {
- this.$submitBtn.enable();
- this.$submitBtnLoader.addClass('hidden');
- }
- }
-
- /**
* Test Integration config
*/
testSettings(formData) {
- this.toggleSubmitBtnState(true);
-
return axios
.put(this.testEndPoint, formData)
.then(({ data }) => {
if (data.error) {
- let flashActions;
-
- if (data.test_failed) {
- flashActions = {
- title: __('Save anyway'),
- clickHandler: e => {
- e.preventDefault();
- this.$form.submit();
- },
- };
- }
-
- flash(`${data.message} ${data.service_response}`, 'alert', document, flashActions);
+ toast(`${data.message} ${data.service_response}`);
} else {
- this.$form.submit();
+ toast(s__('Integrations|Connection successful.'));
}
-
- this.toggleSubmitBtnState(false);
})
.catch(() => {
- flash(__('Something went wrong on our end.'));
- this.toggleSubmitBtnState(false);
+ toast(__('Something went wrong on our end.'));
+ })
+ .finally(() => {
+ this.vue.$store.dispatch('setIsTesting', false);
});
}
}