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:
Diffstat (limited to 'app/assets/javascripts/crm')
-rw-r--r--app/assets/javascripts/crm/components/contact_form.vue224
-rw-r--r--app/assets/javascripts/crm/components/form.vue67
-rw-r--r--app/assets/javascripts/crm/components/new_organization_form.vue164
-rw-r--r--app/assets/javascripts/crm/contacts/bundle.js (renamed from app/assets/javascripts/crm/contacts_bundle.js)0
-rw-r--r--app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue78
-rw-r--r--app/assets/javascripts/crm/contacts/components/contacts_root.vue (renamed from app/assets/javascripts/crm/components/contacts_root.vue)93
-rw-r--r--app/assets/javascripts/crm/contacts/components/graphql/create_contact.mutation.graphql (renamed from app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql)0
-rw-r--r--app/assets/javascripts/crm/contacts/components/graphql/crm_contact_fields.fragment.graphql (renamed from app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql)0
-rw-r--r--app/assets/javascripts/crm/contacts/components/graphql/get_group_contacts.query.graphql (renamed from app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql)0
-rw-r--r--app/assets/javascripts/crm/contacts/components/graphql/update_contact.mutation.graphql (renamed from app/assets/javascripts/crm/components/queries/update_contact.mutation.graphql)0
-rw-r--r--app/assets/javascripts/crm/contacts/routes.js (renamed from app/assets/javascripts/crm/routes.js)6
-rw-r--r--app/assets/javascripts/crm/organizations/bundle.js (renamed from app/assets/javascripts/crm/organizations_bundle.js)0
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/create_organization.mutation.graphql (renamed from app/assets/javascripts/crm/components/queries/create_organization.mutation.graphql)0
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/crm_organization_fields.fragment.graphql (renamed from app/assets/javascripts/crm/components/queries/crm_organization_fields.fragment.graphql)0
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql (renamed from app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql)0
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/update_organization.mutation.graphql10
-rw-r--r--app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue80
-rw-r--r--app/assets/javascripts/crm/organizations/components/organizations_root.vue (renamed from app/assets/javascripts/crm/components/organizations_root.vue)58
-rw-r--r--app/assets/javascripts/crm/organizations/routes.js20
19 files changed, 299 insertions, 501 deletions
diff --git a/app/assets/javascripts/crm/components/contact_form.vue b/app/assets/javascripts/crm/components/contact_form.vue
deleted file mode 100644
index 81ae5c246be..00000000000
--- a/app/assets/javascripts/crm/components/contact_form.vue
+++ /dev/null
@@ -1,224 +0,0 @@
-<script>
-import { GlAlert, GlButton, GlDrawer, GlFormGroup, GlFormInput } from '@gitlab/ui';
-import { produce } from 'immer';
-import { __, s__ } from '~/locale';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
-import { TYPE_GROUP } from '~/graphql_shared/constants';
-import createContactMutation from './queries/create_contact.mutation.graphql';
-import updateContactMutation from './queries/update_contact.mutation.graphql';
-import getGroupContactsQuery from './queries/get_group_contacts.query.graphql';
-
-export default {
- components: {
- GlAlert,
- GlButton,
- GlDrawer,
- GlFormGroup,
- GlFormInput,
- },
- inject: ['groupFullPath', 'groupId'],
- props: {
- drawerOpen: {
- type: Boolean,
- required: true,
- },
- contact: {
- type: Object,
- required: false,
- default: () => {},
- },
- },
- data() {
- return {
- firstName: '',
- lastName: '',
- phone: '',
- email: '',
- description: '',
- submitting: false,
- errorMessages: [],
- };
- },
- computed: {
- invalid() {
- const { firstName, lastName, email } = this;
-
- return firstName.trim() === '' || lastName.trim() === '' || email.trim() === '';
- },
- editMode() {
- return Boolean(this.contact);
- },
- title() {
- return this.editMode ? this.$options.i18n.editTitle : this.$options.i18n.newTitle;
- },
- buttonLabel() {
- return this.editMode
- ? this.$options.i18n.editButtonLabel
- : this.$options.i18n.createButtonLabel;
- },
- mutation() {
- return this.editMode ? updateContactMutation : createContactMutation;
- },
- variables() {
- const { contact, firstName, lastName, phone, email, description, editMode, groupId } = this;
-
- const variables = {
- input: {
- firstName,
- lastName,
- phone,
- email,
- description,
- },
- };
-
- if (editMode) {
- variables.input.id = contact.id;
- } else {
- variables.input.groupId = convertToGraphQLId(TYPE_GROUP, groupId);
- }
-
- return variables;
- },
- },
- mounted() {
- if (this.editMode) {
- const { contact } = this;
-
- this.firstName = contact.firstName || '';
- this.lastName = contact.lastName || '';
- this.phone = contact.phone || '';
- this.email = contact.email || '';
- this.description = contact.description || '';
- }
- },
- methods: {
- save() {
- const { mutation, variables, updateCache, close } = this;
-
- this.submitting = true;
-
- return this.$apollo
- .mutate({
- mutation,
- variables,
- update: updateCache,
- })
- .then(({ data }) => {
- if (
- data.customerRelationsContactCreate?.errors.length === 0 ||
- data.customerRelationsContactUpdate?.errors.length === 0
- ) {
- close(true);
- }
-
- this.submitting = false;
- })
- .catch(() => {
- this.errorMessages = [this.$options.i18n.somethingWentWrong];
- this.submitting = false;
- });
- },
- close(success) {
- this.$emit('close', success);
- },
- updateCache(store, { data }) {
- const mutationData =
- data.customerRelationsContactCreate || data.customerRelationsContactUpdate;
-
- if (mutationData?.errors.length > 0) {
- this.errorMessages = mutationData.errors;
- return;
- }
-
- const queryArgs = {
- query: getGroupContactsQuery,
- variables: { groupFullPath: this.groupFullPath },
- };
-
- const sourceData = store.readQuery(queryArgs);
-
- queryArgs.data = produce(sourceData, (draftState) => {
- draftState.group.contacts.nodes = [
- ...sourceData.group.contacts.nodes.filter(({ id }) => id !== this.contact?.id),
- mutationData.contact,
- ];
- });
-
- store.writeQuery(queryArgs);
- },
- getDrawerHeaderHeight() {
- const wrapperEl = document.querySelector('.content-wrapper');
-
- if (wrapperEl) {
- return `${wrapperEl.offsetTop}px`;
- }
-
- return '';
- },
- },
- i18n: {
- createButtonLabel: s__('Crm|Create new contact'),
- editButtonLabel: __('Save changes'),
- cancel: __('Cancel'),
- firstName: s__('Crm|First name'),
- lastName: s__('Crm|Last name'),
- email: s__('Crm|Email'),
- phone: s__('Crm|Phone number (optional)'),
- description: s__('Crm|Description (optional)'),
- newTitle: s__('Crm|New contact'),
- editTitle: s__('Crm|Edit contact'),
- somethingWentWrong: __('Something went wrong. Please try again.'),
- },
-};
-</script>
-
-<template>
- <gl-drawer
- class="gl-drawer-responsive"
- :open="drawerOpen"
- :header-height="getDrawerHeaderHeight()"
- @close="close(false)"
- >
- <template #title>
- <h3>{{ title }}</h3>
- </template>
- <gl-alert v-if="errorMessages.length" variant="danger" @dismiss="errorMessages = []">
- <ul class="gl-mb-0! gl-ml-5">
- <li v-for="error in errorMessages" :key="error">
- {{ error }}
- </li>
- </ul>
- </gl-alert>
- <form @submit.prevent="save">
- <gl-form-group :label="$options.i18n.firstName" label-for="contact-first-name">
- <gl-form-input id="contact-first-name" v-model="firstName" />
- </gl-form-group>
- <gl-form-group :label="$options.i18n.lastName" label-for="contact-last-name">
- <gl-form-input id="contact-last-name" v-model="lastName" />
- </gl-form-group>
- <gl-form-group :label="$options.i18n.email" label-for="contact-email">
- <gl-form-input id="contact-email" v-model="email" />
- </gl-form-group>
- <gl-form-group :label="$options.i18n.phone" label-for="contact-phone">
- <gl-form-input id="contact-phone" v-model="phone" />
- </gl-form-group>
- <gl-form-group :label="$options.i18n.description" label-for="contact-description">
- <gl-form-input id="contact-description" v-model="description" />
- </gl-form-group>
- <span class="gl-float-right">
- <gl-button data-testid="cancel-button" @click="close(false)">
- {{ $options.i18n.cancel }}
- </gl-button>
- <gl-button
- variant="confirm"
- :disabled="invalid"
- :loading="submitting"
- data-testid="save-contact-button"
- type="submit"
- >{{ buttonLabel }}</gl-button
- >
- </span>
- </form>
- </gl-drawer>
-</template>
diff --git a/app/assets/javascripts/crm/components/form.vue b/app/assets/javascripts/crm/components/form.vue
index b24de1e95e8..4f94898ff63 100644
--- a/app/assets/javascripts/crm/components/form.vue
+++ b/app/assets/javascripts/crm/components/form.vue
@@ -61,11 +61,6 @@ export default {
required: false,
default: null,
},
- existingModel: {
- type: Object,
- required: false,
- default: () => ({}),
- },
additionalCreateParams: {
type: Object,
required: false,
@@ -76,25 +71,42 @@ export default {
required: false,
default: () => MSG_SAVE_CHANGES,
},
+ existingId: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
data() {
- const initialModel = this.fields.reduce(
- (map, field) =>
- Object.assign(map, {
- [field.name]: this.existingModel ? this.existingModel[field.name] : null,
- }),
- {},
- );
-
return {
- model: initialModel,
+ model: null,
submitting: false,
errorMessages: [],
+ records: [],
+ loading: true,
};
},
+ apollo: {
+ records: {
+ query() {
+ return this.getQuery.query;
+ },
+ variables() {
+ return this.getQuery.variables;
+ },
+ update(data) {
+ this.records = getPropValueByPath(data, this.getQueryNodePath).nodes || [];
+ this.setInitialModel();
+ this.loading = false;
+ },
+ error() {
+ this.errorMessages = [MSG_ERROR];
+ },
+ },
+ },
computed: {
isEditMode() {
- return this.existingModel?.id;
+ return this.existingId;
},
isInvalid() {
const { fields, model } = this;
@@ -115,13 +127,24 @@ export default {
);
if (isEditMode) {
- return { input: { id: this.existingModel.id, ...variables } };
+ return { input: { id: this.existingId, ...variables } };
}
return { input: { ...additionalCreateParams, ...variables } };
},
},
methods: {
+ setInitialModel() {
+ const existingModel = this.records.find(({ id }) => id === this.existingId);
+
+ this.model = this.fields.reduce(
+ (map, field) =>
+ Object.assign(map, {
+ [field.name]: !this.isEditMode || !existingModel ? null : existingModel[field.name],
+ }),
+ {},
+ );
+ },
formatValue(model, field) {
if (!isEmpty(model[field.name]) && field.input?.type === 'number') {
return parseFloat(model[field.name]);
@@ -173,7 +196,7 @@ export default {
const sourceData = store.readQuery(getQuery);
const newData = produce(sourceData, (draftState) => {
- getPropValueByPath(draftState, getQueryNodePath).nodes.push(getFirstPropertyValue(result));
+ getPropValueByPath(draftState, getQueryNodePath).nodes.push(this.getPayload(result));
});
store.writeQuery({
@@ -185,6 +208,14 @@ export default {
const optionalSuffix = field.required ? '' : ` ${MSG_OPTIONAL}`;
return field.label + optionalSuffix;
},
+ getPayload(data) {
+ if (!data) return null;
+
+ const keys = Object.keys(data);
+ if (keys[0] === '__typename') return data[keys[1]];
+
+ return data[keys[0]];
+ },
},
MSG_CANCEL,
INDEX_ROUTE_NAME,
@@ -192,7 +223,7 @@ export default {
</script>
<template>
- <mounting-portal mount-to="#js-crm-form-portal" append>
+ <mounting-portal v-if="!loading" mount-to="#js-crm-form-portal" append>
<gl-drawer class="gl-drawer-responsive gl-absolute" :open="drawerOpen" @close="close(false)">
<template #title>
<h3>{{ title }}</h3>
diff --git a/app/assets/javascripts/crm/components/new_organization_form.vue b/app/assets/javascripts/crm/components/new_organization_form.vue
deleted file mode 100644
index 3b11edc6935..00000000000
--- a/app/assets/javascripts/crm/components/new_organization_form.vue
+++ /dev/null
@@ -1,164 +0,0 @@
-<script>
-import { GlAlert, GlButton, GlDrawer, GlFormGroup, GlFormInput } from '@gitlab/ui';
-import { produce } from 'immer';
-import { __, s__ } from '~/locale';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
-import { TYPE_GROUP } from '~/graphql_shared/constants';
-import createOrganization from './queries/create_organization.mutation.graphql';
-import getGroupOrganizationsQuery from './queries/get_group_organizations.query.graphql';
-
-export default {
- components: {
- GlAlert,
- GlButton,
- GlDrawer,
- GlFormGroup,
- GlFormInput,
- },
- inject: ['groupFullPath', 'groupId'],
- props: {
- drawerOpen: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- name: '',
- defaultRate: null,
- description: '',
- submitting: false,
- errorMessages: [],
- };
- },
- computed: {
- invalid() {
- return this.name.trim() === '';
- },
- },
- methods: {
- save() {
- this.submitting = true;
- return this.$apollo
- .mutate({
- mutation: createOrganization,
- variables: {
- input: {
- groupId: convertToGraphQLId(TYPE_GROUP, this.groupId),
- name: this.name,
- defaultRate: this.defaultRate ? parseFloat(this.defaultRate) : null,
- description: this.description,
- },
- },
- update: this.updateCache,
- })
- .then(({ data }) => {
- if (data.customerRelationsOrganizationCreate.errors.length === 0) this.close(true);
-
- this.submitting = false;
- })
- .catch(() => {
- this.errorMessages = [this.$options.i18n.somethingWentWrong];
- this.submitting = false;
- });
- },
- close(success) {
- this.$emit('close', success);
- },
- updateCache(store, { data: { customerRelationsOrganizationCreate } }) {
- if (customerRelationsOrganizationCreate.errors.length > 0) {
- this.errorMessages = customerRelationsOrganizationCreate.errors;
- return;
- }
-
- const variables = {
- groupFullPath: this.groupFullPath,
- };
- const sourceData = store.readQuery({
- query: getGroupOrganizationsQuery,
- variables,
- });
-
- const data = produce(sourceData, (draftState) => {
- draftState.group.organizations.nodes = [
- ...sourceData.group.organizations.nodes,
- customerRelationsOrganizationCreate.organization,
- ];
- });
-
- store.writeQuery({
- query: getGroupOrganizationsQuery,
- variables,
- data,
- });
- },
- getDrawerHeaderHeight() {
- const wrapperEl = document.querySelector('.content-wrapper');
-
- if (wrapperEl) {
- return `${wrapperEl.offsetTop}px`;
- }
-
- return '';
- },
- },
- i18n: {
- buttonLabel: s__('Crm|Create organization'),
- cancel: __('Cancel'),
- name: __('Name'),
- defaultRate: s__('Crm|Default rate (optional)'),
- description: __('Description (optional)'),
- title: s__('Crm|New Organization'),
- somethingWentWrong: __('Something went wrong. Please try again.'),
- },
-};
-</script>
-
-<template>
- <gl-drawer
- class="gl-drawer-responsive"
- :open="drawerOpen"
- :header-height="getDrawerHeaderHeight()"
- @close="close(false)"
- >
- <template #title>
- <h4>{{ $options.i18n.title }}</h4>
- </template>
- <gl-alert v-if="errorMessages.length" variant="danger" @dismiss="errorMessages = []">
- <ul class="gl-mb-0! gl-ml-5">
- <li v-for="error in errorMessages" :key="error">
- {{ error }}
- </li>
- </ul>
- </gl-alert>
- <form @submit.prevent="save">
- <gl-form-group :label="$options.i18n.name" label-for="organization-name">
- <gl-form-input id="organization-name" v-model="name" />
- </gl-form-group>
- <gl-form-group :label="$options.i18n.defaultRate" label-for="organization-default-rate">
- <gl-form-input
- id="organization-default-rate"
- v-model="defaultRate"
- type="number"
- step="0.01"
- />
- </gl-form-group>
- <gl-form-group :label="$options.i18n.description" label-for="organization-description">
- <gl-form-input id="organization-description" v-model="description" />
- </gl-form-group>
- <span class="gl-float-right">
- <gl-button data-testid="cancel-button" @click="close(false)">
- {{ $options.i18n.cancel }}
- </gl-button>
- <gl-button
- variant="confirm"
- :disabled="invalid"
- :loading="submitting"
- data-testid="create-new-organization-button"
- type="submit"
- >{{ $options.i18n.buttonLabel }}</gl-button
- >
- </span>
- </form>
- </gl-drawer>
-</template>
diff --git a/app/assets/javascripts/crm/contacts_bundle.js b/app/assets/javascripts/crm/contacts/bundle.js
index f49ec64210f..f49ec64210f 100644
--- a/app/assets/javascripts/crm/contacts_bundle.js
+++ b/app/assets/javascripts/crm/contacts/bundle.js
diff --git a/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue b/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue
new file mode 100644
index 00000000000..58eaabfbb7f
--- /dev/null
+++ b/app/assets/javascripts/crm/contacts/components/contact_form_wrapper.vue
@@ -0,0 +1,78 @@
+<script>
+import { s__, __ } from '~/locale';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { TYPE_CRM_CONTACT, TYPE_GROUP } from '~/graphql_shared/constants';
+import ContactForm from '../../components/form.vue';
+import getGroupContactsQuery from './graphql/get_group_contacts.query.graphql';
+import createContactMutation from './graphql/create_contact.mutation.graphql';
+import updateContactMutation from './graphql/update_contact.mutation.graphql';
+
+export default {
+ components: {
+ ContactForm,
+ },
+ inject: ['groupFullPath', 'groupId'],
+ props: {
+ isEditMode: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ contactGraphQLId() {
+ if (!this.isEditMode) return null;
+
+ return convertToGraphQLId(TYPE_CRM_CONTACT, this.$route.params.id);
+ },
+ groupGraphQLId() {
+ return convertToGraphQLId(TYPE_GROUP, this.groupId);
+ },
+ mutation() {
+ if (this.isEditMode) return updateContactMutation;
+
+ return createContactMutation;
+ },
+ getQuery() {
+ return {
+ query: getGroupContactsQuery,
+ variables: { groupFullPath: this.groupFullPath },
+ };
+ },
+ title() {
+ if (this.isEditMode) return s__('Crm|Edit contact');
+
+ return s__('Crm|New contact');
+ },
+ successMessage() {
+ if (this.isEditMode) return s__('Crm|Contact has been updated.');
+
+ return s__('Crm|Contact has been added.');
+ },
+ additionalCreateParams() {
+ return { groupId: this.groupGraphQLId };
+ },
+ },
+ fields: [
+ { name: 'firstName', label: __('First name'), required: true },
+ { name: 'lastName', label: __('Last name'), required: true },
+ { name: 'email', label: __('Email'), required: true },
+ { name: 'phone', label: __('Phone') },
+ { name: 'description', label: __('Description') },
+ ],
+};
+</script>
+
+<template>
+ <contact-form
+ :drawer-open="true"
+ :get-query="getQuery"
+ get-query-node-path="group.contacts"
+ :mutation="mutation"
+ :additional-create-params="additionalCreateParams"
+ :existing-id="contactGraphQLId"
+ :fields="$options.fields"
+ :title="title"
+ :success-message="successMessage"
+ />
+</template>
diff --git a/app/assets/javascripts/crm/components/contacts_root.vue b/app/assets/javascripts/crm/contacts/components/contacts_root.vue
index 178ce84c64d..17be3800256 100644
--- a/app/assets/javascripts/crm/components/contacts_root.vue
+++ b/app/assets/javascripts/crm/contacts/components/contacts_root.vue
@@ -2,11 +2,9 @@
import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { parseBoolean } from '~/lib/utils/common_utils';
import { s__, __ } from '~/locale';
-import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { TYPE_CRM_CONTACT } from '~/graphql_shared/constants';
-import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from '../constants';
-import getGroupContactsQuery from './queries/get_group_contacts.query.graphql';
-import ContactForm from './contact_form.vue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME } from '../../constants';
+import getGroupContactsQuery from './graphql/get_group_contacts.query.graphql';
export default {
components: {
@@ -14,12 +12,11 @@ export default {
GlButton,
GlLoadingIcon,
GlTable,
- ContactForm,
},
directives: {
GlTooltip: GlTooltipDirective,
},
- inject: ['groupFullPath', 'groupIssuesPath', 'canAdminCrmContact'],
+ inject: ['canAdminCrmContact', 'groupFullPath', 'groupIssuesPath'],
data() {
return {
contacts: [],
@@ -48,50 +45,20 @@ export default {
isLoading() {
return this.$apollo.queries.contacts.loading;
},
- showNewForm() {
- return this.$route.name === NEW_ROUTE_NAME;
- },
- showEditForm() {
- return !this.isLoading && this.$route.name === EDIT_ROUTE_NAME;
- },
canAdmin() {
return parseBoolean(this.canAdminCrmContact);
},
- editingContact() {
- return this.contacts.find(
- (contact) => contact.id === convertToGraphQLId(TYPE_CRM_CONTACT, this.$route.params.id),
- );
- },
},
methods: {
extractContacts(data) {
const contacts = data?.group?.contacts?.nodes || [];
return contacts.slice().sort((a, b) => a.firstName.localeCompare(b.firstName));
},
- displayNewForm() {
- if (this.showNewForm) return;
-
- this.$router.push({ name: NEW_ROUTE_NAME });
- },
- hideNewForm(success) {
- if (success) this.$toast.show(s__('Crm|Contact has been added'));
-
- this.$router.replace({ name: INDEX_ROUTE_NAME });
- },
- hideEditForm(success) {
- if (success) this.$toast.show(s__('Crm|Contact has been updated'));
-
- this.editingContactId = 0;
- this.$router.replace({ name: INDEX_ROUTE_NAME });
- },
getIssuesPath(path, value) {
return `${path}?scope=all&state=opened&crm_contact_id=${value}`;
},
- edit(value) {
- if (this.showEditForm) return;
-
- this.editingContactId = value;
- this.$router.push({ name: EDIT_ROUTE_NAME, params: { id: value } });
+ getEditRoute(id) {
+ return { name: this.$options.EDIT_ROUTE_NAME, params: { id } };
},
},
fields: [
@@ -119,10 +86,12 @@ export default {
emptyText: s__('Crm|No contacts found'),
issuesButtonLabel: __('View issues'),
editButtonLabel: __('Edit'),
- title: s__('Crm|Customer Relations Contacts'),
+ title: s__('Crm|Customer relations contacts'),
newContact: s__('Crm|New contact'),
errorText: __('Something went wrong. Please try again.'),
},
+ EDIT_ROUTE_NAME,
+ NEW_ROUTE_NAME,
};
</script>
@@ -137,24 +106,15 @@ export default {
<h2 class="gl-font-size-h2 gl-my-0">
{{ $options.i18n.title }}
</h2>
- <div class="gl-display-none gl-md-display-flex gl-align-items-center gl-justify-content-end">
- <gl-button
- v-if="canAdmin"
- variant="confirm"
- data-testid="new-contact-button"
- @click="displayNewForm"
- >
- {{ $options.i18n.newContact }}
- </gl-button>
+ <div v-if="canAdmin">
+ <router-link :to="{ name: $options.NEW_ROUTE_NAME }">
+ <gl-button variant="confirm" data-testid="new-contact-button">
+ {{ $options.i18n.newContact }}
+ </gl-button>
+ </router-link>
</div>
</div>
- <contact-form v-if="showNewForm" :drawer-open="showNewForm" @close="hideNewForm" />
- <contact-form
- v-if="showEditForm"
- :contact="editingContact"
- :drawer-open="showEditForm"
- @close="hideEditForm"
- />
+ <router-view />
<gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" />
<gl-table
v-else
@@ -164,23 +124,24 @@ export default {
:empty-text="$options.i18n.emptyText"
show-empty
>
- <template #cell(id)="data">
+ <template #cell(id)="{ value: id }">
<gl-button
v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel"
class="gl-mr-3"
data-testid="issues-link"
icon="issues"
:aria-label="$options.i18n.issuesButtonLabel"
- :href="getIssuesPath(groupIssuesPath, data.value)"
- />
- <gl-button
- v-if="canAdmin"
- v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
- data-testid="edit-contact-button"
- icon="pencil"
- :aria-label="$options.i18n.editButtonLabel"
- @click="edit(data.value)"
+ :href="getIssuesPath(groupIssuesPath, id)"
/>
+ <router-link :to="getEditRoute(id)">
+ <gl-button
+ v-if="canAdmin"
+ v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
+ data-testid="edit-contact-button"
+ icon="pencil"
+ :aria-label="$options.i18n.editButtonLabel"
+ />
+ </router-link>
</template>
</gl-table>
</div>
diff --git a/app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql b/app/assets/javascripts/crm/contacts/components/graphql/create_contact.mutation.graphql
index e0192459609..e0192459609 100644
--- a/app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql
+++ b/app/assets/javascripts/crm/contacts/components/graphql/create_contact.mutation.graphql
diff --git a/app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql b/app/assets/javascripts/crm/contacts/components/graphql/crm_contact_fields.fragment.graphql
index cef4083b446..cef4083b446 100644
--- a/app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql
+++ b/app/assets/javascripts/crm/contacts/components/graphql/crm_contact_fields.fragment.graphql
diff --git a/app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql b/app/assets/javascripts/crm/contacts/components/graphql/get_group_contacts.query.graphql
index 2a8150e42e3..2a8150e42e3 100644
--- a/app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql
+++ b/app/assets/javascripts/crm/contacts/components/graphql/get_group_contacts.query.graphql
diff --git a/app/assets/javascripts/crm/components/queries/update_contact.mutation.graphql b/app/assets/javascripts/crm/contacts/components/graphql/update_contact.mutation.graphql
index f55f6a10e0a..f55f6a10e0a 100644
--- a/app/assets/javascripts/crm/components/queries/update_contact.mutation.graphql
+++ b/app/assets/javascripts/crm/contacts/components/graphql/update_contact.mutation.graphql
diff --git a/app/assets/javascripts/crm/routes.js b/app/assets/javascripts/crm/contacts/routes.js
index 12aa17d73b6..18768e1c775 100644
--- a/app/assets/javascripts/crm/routes.js
+++ b/app/assets/javascripts/crm/contacts/routes.js
@@ -1,4 +1,5 @@
-import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from './constants';
+import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from '../constants';
+import ContactFormWrapper from './components/contact_form_wrapper.vue';
export default [
{
@@ -8,9 +9,12 @@ export default [
{
name: NEW_ROUTE_NAME,
path: '/new',
+ component: ContactFormWrapper,
},
{
name: EDIT_ROUTE_NAME,
path: '/:id/edit',
+ component: ContactFormWrapper,
+ props: { isEditMode: true },
},
];
diff --git a/app/assets/javascripts/crm/organizations_bundle.js b/app/assets/javascripts/crm/organizations/bundle.js
index 828d7cd426c..828d7cd426c 100644
--- a/app/assets/javascripts/crm/organizations_bundle.js
+++ b/app/assets/javascripts/crm/organizations/bundle.js
diff --git a/app/assets/javascripts/crm/components/queries/create_organization.mutation.graphql b/app/assets/javascripts/crm/organizations/components/graphql/create_organization.mutation.graphql
index 2cc7e53ee9b..2cc7e53ee9b 100644
--- a/app/assets/javascripts/crm/components/queries/create_organization.mutation.graphql
+++ b/app/assets/javascripts/crm/organizations/components/graphql/create_organization.mutation.graphql
diff --git a/app/assets/javascripts/crm/components/queries/crm_organization_fields.fragment.graphql b/app/assets/javascripts/crm/organizations/components/graphql/crm_organization_fields.fragment.graphql
index 4adc5742d3a..4adc5742d3a 100644
--- a/app/assets/javascripts/crm/components/queries/crm_organization_fields.fragment.graphql
+++ b/app/assets/javascripts/crm/organizations/components/graphql/crm_organization_fields.fragment.graphql
diff --git a/app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
index e8d8109431e..e8d8109431e 100644
--- a/app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql
+++ b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
diff --git a/app/assets/javascripts/crm/organizations/components/graphql/update_organization.mutation.graphql b/app/assets/javascripts/crm/organizations/components/graphql/update_organization.mutation.graphql
new file mode 100644
index 00000000000..a4c46d1f0fa
--- /dev/null
+++ b/app/assets/javascripts/crm/organizations/components/graphql/update_organization.mutation.graphql
@@ -0,0 +1,10 @@
+#import "./crm_organization_fields.fragment.graphql"
+
+mutation updateOrganization($input: CustomerRelationsOrganizationUpdateInput!) {
+ customerRelationsOrganizationUpdate(input: $input) {
+ organization {
+ ...OrganizationFragment
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
new file mode 100644
index 00000000000..38468e1f4e4
--- /dev/null
+++ b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
@@ -0,0 +1,80 @@
+<script>
+import { s__, __ } from '~/locale';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { TYPE_CRM_ORGANIZATION, TYPE_GROUP } from '~/graphql_shared/constants';
+import OrganizationForm from '../../components/form.vue';
+import getGroupOrganizationsQuery from './graphql/get_group_organizations.query.graphql';
+import createOrganizationMutation from './graphql/create_organization.mutation.graphql';
+import updateOrganizationMutation from './graphql/update_organization.mutation.graphql';
+
+export default {
+ components: {
+ OrganizationForm,
+ },
+ inject: ['groupFullPath', 'groupId'],
+ props: {
+ isEditMode: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ organizationGraphQLId() {
+ if (!this.isEditMode) return null;
+
+ return convertToGraphQLId(TYPE_CRM_ORGANIZATION, this.$route.params.id);
+ },
+ groupGraphQLId() {
+ return convertToGraphQLId(TYPE_GROUP, this.groupId);
+ },
+ mutation() {
+ if (this.isEditMode) return updateOrganizationMutation;
+
+ return createOrganizationMutation;
+ },
+ getQuery() {
+ return {
+ query: getGroupOrganizationsQuery,
+ variables: { groupFullPath: this.groupFullPath },
+ };
+ },
+ title() {
+ if (this.isEditMode) return s__('Crm|Edit organization');
+
+ return s__('Crm|New organization');
+ },
+ successMessage() {
+ if (this.isEditMode) return s__('Crm|Organization has been updated.');
+
+ return s__('Crm|Organization has been added.');
+ },
+ additionalCreateParams() {
+ return { groupId: this.groupGraphQLId };
+ },
+ },
+ fields: [
+ { name: 'name', label: __('Name'), required: true },
+ {
+ name: 'defaultRate',
+ label: s__('Crm|Default rate'),
+ input: { type: 'number', step: '0.01' },
+ },
+ { name: 'description', label: __('Description') },
+ ],
+};
+</script>
+
+<template>
+ <organization-form
+ :drawer-open="true"
+ :get-query="getQuery"
+ get-query-node-path="group.organizations"
+ :mutation="mutation"
+ :additional-create-params="additionalCreateParams"
+ :existing-id="organizationGraphQLId"
+ :fields="$options.fields"
+ :title="title"
+ :success-message="successMessage"
+ />
+</template>
diff --git a/app/assets/javascripts/crm/components/organizations_root.vue b/app/assets/javascripts/crm/organizations/components/organizations_root.vue
index 9370c6377e9..522e29eb2af 100644
--- a/app/assets/javascripts/crm/components/organizations_root.vue
+++ b/app/assets/javascripts/crm/organizations/components/organizations_root.vue
@@ -3,9 +3,8 @@ import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@
import { parseBoolean } from '~/lib/utils/common_utils';
import { s__, __ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME } from '../constants';
-import getGroupOrganizationsQuery from './queries/get_group_organizations.query.graphql';
-import NewOrganizationForm from './new_organization_form.vue';
+import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME } from '../../constants';
+import getGroupOrganizationsQuery from './graphql/get_group_organizations.query.graphql';
export default {
components: {
@@ -13,7 +12,6 @@ export default {
GlButton,
GlLoadingIcon,
GlTable,
- NewOrganizationForm,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -21,8 +19,8 @@ export default {
inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath'],
data() {
return {
- error: false,
organizations: [],
+ error: false,
};
},
apollo: {
@@ -47,10 +45,7 @@ export default {
isLoading() {
return this.$apollo.queries.organizations.loading;
},
- showNewForm() {
- return this.$route.name === NEW_ROUTE_NAME;
- },
- canCreateNew() {
+ canAdmin() {
return parseBoolean(this.canAdminCrmOrganization);
},
},
@@ -62,15 +57,8 @@ export default {
getIssuesPath(path, value) {
return `${path}?scope=all&state=opened&crm_organization_id=${value}`;
},
- displayNewForm() {
- if (this.showNewForm) return;
-
- this.$router.push({ name: NEW_ROUTE_NAME });
- },
- hideNewForm(success) {
- if (success) this.$toast.show(this.$options.i18n.organizationAdded);
-
- this.$router.replace({ name: INDEX_ROUTE_NAME });
+ getEditRoute(id) {
+ return { name: this.$options.EDIT_ROUTE_NAME, params: { id } };
},
},
fields: [
@@ -79,7 +67,7 @@ export default {
{ key: 'description', sortable: true },
{
key: 'id',
- label: __('Issues'),
+ label: '',
formatter: (id) => {
return getIdFromGraphQLId(id);
},
@@ -88,11 +76,13 @@ export default {
i18n: {
emptyText: s__('Crm|No organizations found'),
issuesButtonLabel: __('View issues'),
- title: s__('Crm|Customer Relations Organizations'),
+ editButtonLabel: __('Edit'),
+ title: s__('Crm|Customer relations organizations'),
newOrganization: s__('Crm|New organization'),
errorText: __('Something went wrong. Please try again.'),
- organizationAdded: s__('Crm|Organization has been added'),
},
+ EDIT_ROUTE_NAME,
+ NEW_ROUTE_NAME,
};
</script>
@@ -108,15 +98,17 @@ export default {
{{ $options.i18n.title }}
</h2>
<div
- v-if="canCreateNew"
+ v-if="canAdmin"
class="gl-display-none gl-md-display-flex gl-align-items-center gl-justify-content-end"
>
- <gl-button variant="confirm" data-testid="new-organization-button" @click="displayNewForm">
- {{ $options.i18n.newOrganization }}
- </gl-button>
+ <router-link :to="{ name: $options.NEW_ROUTE_NAME }">
+ <gl-button variant="confirm" data-testid="new-organization-button">
+ {{ $options.i18n.newOrganization }}
+ </gl-button>
+ </router-link>
</div>
</div>
- <new-organization-form v-if="showNewForm" :drawer-open="showNewForm" @close="hideNewForm" />
+ <router-view />
<gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" />
<gl-table
v-else
@@ -126,14 +118,24 @@ export default {
:empty-text="$options.i18n.emptyText"
show-empty
>
- <template #cell(id)="data">
+ <template #cell(id)="{ value: id }">
<gl-button
v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel"
+ class="gl-mr-3"
data-testid="issues-link"
icon="issues"
:aria-label="$options.i18n.issuesButtonLabel"
- :href="getIssuesPath(groupIssuesPath, data.value)"
+ :href="getIssuesPath(groupIssuesPath, id)"
/>
+ <router-link :to="getEditRoute(id)">
+ <gl-button
+ v-if="canAdmin"
+ v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
+ data-testid="edit-organization-button"
+ icon="pencil"
+ :aria-label="$options.i18n.editButtonLabel"
+ />
+ </router-link>
</template>
</gl-table>
</div>
diff --git a/app/assets/javascripts/crm/organizations/routes.js b/app/assets/javascripts/crm/organizations/routes.js
new file mode 100644
index 00000000000..85bd3b32877
--- /dev/null
+++ b/app/assets/javascripts/crm/organizations/routes.js
@@ -0,0 +1,20 @@
+import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from '../constants';
+import OrganizationFormWrapper from './components/organization_form_wrapper.vue';
+
+export default [
+ {
+ name: INDEX_ROUTE_NAME,
+ path: '/',
+ },
+ {
+ name: NEW_ROUTE_NAME,
+ path: '/new',
+ component: OrganizationFormWrapper,
+ },
+ {
+ name: EDIT_ROUTE_NAME,
+ path: '/:id/edit',
+ component: OrganizationFormWrapper,
+ props: { isEditMode: true },
+ },
+];