diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 16:37:47 +0300 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/crm | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) |
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/crm')
15 files changed, 692 insertions, 41 deletions
diff --git a/app/assets/javascripts/crm/components/contact_form.vue b/app/assets/javascripts/crm/components/contact_form.vue new file mode 100644 index 00000000000..81ae5c246be --- /dev/null +++ b/app/assets/javascripts/crm/components/contact_form.vue @@ -0,0 +1,224 @@ +<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/contacts_root.vue b/app/assets/javascripts/crm/components/contacts_root.vue index 83c02f7d5fe..178ce84c64d 100644 --- a/app/assets/javascripts/crm/components/contacts_root.vue +++ b/app/assets/javascripts/crm/components/contacts_root.vue @@ -1,17 +1,30 @@ <script> -import { GlLoadingIcon, GlTable } from '@gitlab/ui'; -import createFlash from '~/flash'; +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'; export default { components: { + GlAlert, + GlButton, GlLoadingIcon, GlTable, + ContactForm, }, - inject: ['groupFullPath'], + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: ['groupFullPath', 'groupIssuesPath', 'canAdminCrmContact'], data() { - return { contacts: [] }; + return { + contacts: [], + error: false, + }; }, apollo: { contacts: { @@ -26,12 +39,8 @@ export default { update(data) { return this.extractContacts(data); }, - error(error) { - createFlash({ - message: __('Something went wrong. Please try again.'), - error, - captureError: true, - }); + error() { + this.error = true; }, }, }, @@ -39,12 +48,51 @@ 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 } }); + }, }, fields: [ { key: 'firstName', sortable: true }, @@ -59,22 +107,81 @@ export default { }, sortable: true, }, + { + key: 'id', + label: '', + formatter: (id) => { + return getIdFromGraphQLId(id); + }, + }, ], i18n: { emptyText: s__('Crm|No contacts found'), + issuesButtonLabel: __('View issues'), + editButtonLabel: __('Edit'), + title: s__('Crm|Customer Relations Contacts'), + newContact: s__('Crm|New contact'), + errorText: __('Something went wrong. Please try again.'), }, }; </script> <template> <div> + <gl-alert v-if="error" variant="danger" class="gl-mt-6" @dismiss="error = false"> + {{ $options.i18n.errorText }} + </gl-alert> + <div + class="gl-display-flex gl-align-items-baseline gl-flex-direction-row gl-justify-content-space-between gl-mt-6" + > + <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> + </div> + <contact-form v-if="showNewForm" :drawer-open="showNewForm" @close="hideNewForm" /> + <contact-form + v-if="showEditForm" + :contact="editingContact" + :drawer-open="showEditForm" + @close="hideEditForm" + /> <gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" /> <gl-table v-else + class="gl-mt-5" :items="contacts" :fields="$options.fields" :empty-text="$options.i18n.emptyText" show-empty - /> + > + <template #cell(id)="data"> + <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)" + /> + </template> + </gl-table> </div> </template> diff --git a/app/assets/javascripts/crm/components/new_organization_form.vue b/app/assets/javascripts/crm/components/new_organization_form.vue new file mode 100644 index 00000000000..3b11edc6935 --- /dev/null +++ b/app/assets/javascripts/crm/components/new_organization_form.vue @@ -0,0 +1,164 @@ +<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/components/organizations_root.vue b/app/assets/javascripts/crm/components/organizations_root.vue index 98b45d0a042..9370c6377e9 100644 --- a/app/assets/javascripts/crm/components/organizations_root.vue +++ b/app/assets/javascripts/crm/components/organizations_root.vue @@ -1,17 +1,29 @@ <script> -import { GlLoadingIcon, GlTable } from '@gitlab/ui'; -import createFlash from '~/flash'; +import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui'; +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'; export default { components: { + GlAlert, + GlButton, GlLoadingIcon, GlTable, + NewOrganizationForm, }, - inject: ['groupFullPath'], + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath'], data() { - return { organizations: [] }; + return { + error: false, + organizations: [], + }; }, apollo: { organizations: { @@ -26,12 +38,8 @@ export default { update(data) { return this.extractOrganizations(data); }, - error(error) { - createFlash({ - message: __('Something went wrong. Please try again.'), - error, - captureError: true, - }); + error() { + this.error = true; }, }, }, @@ -39,33 +47,94 @@ export default { isLoading() { return this.$apollo.queries.organizations.loading; }, + showNewForm() { + return this.$route.name === NEW_ROUTE_NAME; + }, + canCreateNew() { + return parseBoolean(this.canAdminCrmOrganization); + }, }, methods: { extractOrganizations(data) { const organizations = data?.group?.organizations?.nodes || []; return organizations.slice().sort((a, b) => a.name.localeCompare(b.name)); }, + 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 }); + }, }, fields: [ { key: 'name', sortable: true }, { key: 'defaultRate', sortable: true }, { key: 'description', sortable: true }, + { + key: 'id', + label: __('Issues'), + formatter: (id) => { + return getIdFromGraphQLId(id); + }, + }, ], i18n: { emptyText: s__('Crm|No organizations found'), + issuesButtonLabel: __('View issues'), + 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'), }, }; </script> <template> <div> + <gl-alert v-if="error" variant="danger" class="gl-mt-6" @dismiss="error = false"> + {{ $options.i18n.errorText }} + </gl-alert> + <div + class="gl-display-flex gl-align-items-baseline gl-flex-direction-row gl-justify-content-space-between gl-mt-6" + > + <h2 class="gl-font-size-h2 gl-my-0"> + {{ $options.i18n.title }} + </h2> + <div + v-if="canCreateNew" + 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> + </div> + </div> + <new-organization-form v-if="showNewForm" :drawer-open="showNewForm" @close="hideNewForm" /> <gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" /> <gl-table v-else + class="gl-mt-5" :items="organizations" :fields="$options.fields" :empty-text="$options.i18n.emptyText" show-empty - /> + > + <template #cell(id)="data"> + <gl-button + v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel" + data-testid="issues-link" + icon="issues" + :aria-label="$options.i18n.issuesButtonLabel" + :href="getIssuesPath(groupIssuesPath, data.value)" + /> + </template> + </gl-table> </div> </template> diff --git a/app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql b/app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql new file mode 100644 index 00000000000..e0192459609 --- /dev/null +++ b/app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql @@ -0,0 +1,10 @@ +#import "./crm_contact_fields.fragment.graphql" + +mutation createContact($input: CustomerRelationsContactCreateInput!) { + customerRelationsContactCreate(input: $input) { + contact { + ...ContactFragment + } + errors + } +} diff --git a/app/assets/javascripts/crm/components/queries/create_organization.mutation.graphql b/app/assets/javascripts/crm/components/queries/create_organization.mutation.graphql new file mode 100644 index 00000000000..2cc7e53ee9b --- /dev/null +++ b/app/assets/javascripts/crm/components/queries/create_organization.mutation.graphql @@ -0,0 +1,10 @@ +#import "./crm_organization_fields.fragment.graphql" + +mutation createOrganization($input: CustomerRelationsOrganizationCreateInput!) { + customerRelationsOrganizationCreate(input: $input) { + organization { + ...OrganizationFragment + } + errors + } +} diff --git a/app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql b/app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql new file mode 100644 index 00000000000..cef4083b446 --- /dev/null +++ b/app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql @@ -0,0 +1,14 @@ +fragment ContactFragment on CustomerRelationsContact { + __typename + id + firstName + lastName + email + phone + description + organization { + __typename + id + name + } +} diff --git a/app/assets/javascripts/crm/components/queries/crm_organization_fields.fragment.graphql b/app/assets/javascripts/crm/components/queries/crm_organization_fields.fragment.graphql new file mode 100644 index 00000000000..4adc5742d3a --- /dev/null +++ b/app/assets/javascripts/crm/components/queries/crm_organization_fields.fragment.graphql @@ -0,0 +1,7 @@ +fragment OrganizationFragment on CustomerRelationsOrganization { + __typename + id + name + defaultRate + description +} diff --git a/app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql b/app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql index f6acd258585..2a8150e42e3 100644 --- a/app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql +++ b/app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql @@ -1,21 +1,12 @@ +#import "./crm_contact_fields.fragment.graphql" + query contacts($groupFullPath: ID!) { group(fullPath: $groupFullPath) { __typename id contacts { nodes { - __typename - id - firstName - lastName - email - phone - description - organization { - __typename - id - name - } + ...ContactFragment } } } diff --git a/app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql b/app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql index 7c4ec6ec585..e8d8109431e 100644 --- a/app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql +++ b/app/assets/javascripts/crm/components/queries/get_group_organizations.query.graphql @@ -1,14 +1,12 @@ +#import "./crm_organization_fields.fragment.graphql" + query organizations($groupFullPath: ID!) { group(fullPath: $groupFullPath) { __typename id organizations { nodes { - __typename - id - name - defaultRate - description + ...OrganizationFragment } } } diff --git a/app/assets/javascripts/crm/components/queries/update_contact.mutation.graphql b/app/assets/javascripts/crm/components/queries/update_contact.mutation.graphql new file mode 100644 index 00000000000..f55f6a10e0a --- /dev/null +++ b/app/assets/javascripts/crm/components/queries/update_contact.mutation.graphql @@ -0,0 +1,10 @@ +#import "./crm_contact_fields.fragment.graphql" + +mutation updateContact($input: CustomerRelationsContactUpdateInput!) { + customerRelationsContactUpdate(input: $input) { + contact { + ...ContactFragment + } + errors + } +} diff --git a/app/assets/javascripts/crm/constants.js b/app/assets/javascripts/crm/constants.js new file mode 100644 index 00000000000..3b085837aea --- /dev/null +++ b/app/assets/javascripts/crm/constants.js @@ -0,0 +1,3 @@ +export const INDEX_ROUTE_NAME = 'index'; +export const NEW_ROUTE_NAME = 'new'; +export const EDIT_ROUTE_NAME = 'edit'; diff --git a/app/assets/javascripts/crm/contacts_bundle.js b/app/assets/javascripts/crm/contacts_bundle.js index 6438953596e..f49ec64210f 100644 --- a/app/assets/javascripts/crm/contacts_bundle.js +++ b/app/assets/javascripts/crm/contacts_bundle.js @@ -1,9 +1,14 @@ +import { GlToast } from '@gitlab/ui'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import VueRouter from 'vue-router'; import createDefaultClient from '~/lib/graphql'; import CrmContactsRoot from './components/contacts_root.vue'; +import routes from './routes'; Vue.use(VueApollo); +Vue.use(VueRouter); +Vue.use(GlToast); export default () => { const el = document.getElementById('js-crm-contacts-app'); @@ -16,10 +21,19 @@ export default () => { return false; } + const { basePath, groupFullPath, groupIssuesPath, canAdminCrmContact, groupId } = el.dataset; + + const router = new VueRouter({ + base: basePath, + mode: 'history', + routes, + }); + return new Vue({ el, + router, apolloProvider, - provide: { groupFullPath: el.dataset.groupFullPath }, + provide: { groupFullPath, groupIssuesPath, canAdminCrmContact, groupId }, render(createElement) { return createElement(CrmContactsRoot); }, diff --git a/app/assets/javascripts/crm/organizations_bundle.js b/app/assets/javascripts/crm/organizations_bundle.js index ac9990b9fb4..828d7cd426c 100644 --- a/app/assets/javascripts/crm/organizations_bundle.js +++ b/app/assets/javascripts/crm/organizations_bundle.js @@ -1,9 +1,14 @@ +import { GlToast } from '@gitlab/ui'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import VueRouter from 'vue-router'; import createDefaultClient from '~/lib/graphql'; import CrmOrganizationsRoot from './components/organizations_root.vue'; +import routes from './routes'; Vue.use(VueApollo); +Vue.use(VueRouter); +Vue.use(GlToast); export default () => { const el = document.getElementById('js-crm-organizations-app'); @@ -16,10 +21,19 @@ export default () => { return false; } + const { basePath, canAdminCrmOrganization, groupFullPath, groupId, groupIssuesPath } = el.dataset; + + const router = new VueRouter({ + base: basePath, + mode: 'history', + routes, + }); + return new Vue({ el, + router, apolloProvider, - provide: { groupFullPath: el.dataset.groupFullPath }, + provide: { canAdminCrmOrganization, groupFullPath, groupId, groupIssuesPath }, render(createElement) { return createElement(CrmOrganizationsRoot); }, diff --git a/app/assets/javascripts/crm/routes.js b/app/assets/javascripts/crm/routes.js new file mode 100644 index 00000000000..12aa17d73b6 --- /dev/null +++ b/app/assets/javascripts/crm/routes.js @@ -0,0 +1,16 @@ +import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from './constants'; + +export default [ + { + name: INDEX_ROUTE_NAME, + path: '/', + }, + { + name: NEW_ROUTE_NAME, + path: '/new', + }, + { + name: EDIT_ROUTE_NAME, + path: '/:id/edit', + }, +]; |