diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
commit | 3cccd102ba543e02725d247893729e5c73b38295 (patch) | |
tree | f36a04ec38517f5deaaacb5acc7d949688d1e187 /app/assets/javascripts/crm/contacts | |
parent | 205943281328046ef7b4528031b90fbda70c75ac (diff) | |
download | gitlab-ce-3cccd102ba543e02725d247893729e5c73b38295.tar.gz |
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'app/assets/javascripts/crm/contacts')
8 files changed, 334 insertions, 0 deletions
diff --git a/app/assets/javascripts/crm/contacts/bundle.js b/app/assets/javascripts/crm/contacts/bundle.js new file mode 100644 index 00000000000..f49ec64210f --- /dev/null +++ b/app/assets/javascripts/crm/contacts/bundle.js @@ -0,0 +1,41 @@ +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'); + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + if (!el) { + 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, groupIssuesPath, canAdminCrmContact, groupId }, + render(createElement) { + return createElement(CrmContactsRoot); + }, + }); +}; 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/contacts/components/contacts_root.vue b/app/assets/javascripts/crm/contacts/components/contacts_root.vue new file mode 100644 index 00000000000..17be3800256 --- /dev/null +++ b/app/assets/javascripts/crm/contacts/components/contacts_root.vue @@ -0,0 +1,148 @@ +<script> +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 { EDIT_ROUTE_NAME, NEW_ROUTE_NAME } from '../../constants'; +import getGroupContactsQuery from './graphql/get_group_contacts.query.graphql'; + +export default { + components: { + GlAlert, + GlButton, + GlLoadingIcon, + GlTable, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: ['canAdminCrmContact', 'groupFullPath', 'groupIssuesPath'], + data() { + return { + contacts: [], + error: false, + }; + }, + apollo: { + contacts: { + query() { + return getGroupContactsQuery; + }, + variables() { + return { + groupFullPath: this.groupFullPath, + }; + }, + update(data) { + return this.extractContacts(data); + }, + error() { + this.error = true; + }, + }, + }, + computed: { + isLoading() { + return this.$apollo.queries.contacts.loading; + }, + canAdmin() { + return parseBoolean(this.canAdminCrmContact); + }, + }, + methods: { + extractContacts(data) { + const contacts = data?.group?.contacts?.nodes || []; + return contacts.slice().sort((a, b) => a.firstName.localeCompare(b.firstName)); + }, + getIssuesPath(path, value) { + return `${path}?scope=all&state=opened&crm_contact_id=${value}`; + }, + getEditRoute(id) { + return { name: this.$options.EDIT_ROUTE_NAME, params: { id } }; + }, + }, + fields: [ + { key: 'firstName', sortable: true }, + { key: 'lastName', sortable: true }, + { key: 'email', sortable: true }, + { key: 'phone', sortable: true }, + { key: 'description', sortable: true }, + { + key: 'organization', + formatter: (organization) => { + return organization?.name; + }, + 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.'), + }, + EDIT_ROUTE_NAME, + NEW_ROUTE_NAME, +}; +</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="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> + <router-view /> + <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)="{ 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, 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> +</template> diff --git a/app/assets/javascripts/crm/contacts/components/graphql/create_contact.mutation.graphql b/app/assets/javascripts/crm/contacts/components/graphql/create_contact.mutation.graphql new file mode 100644 index 00000000000..e0192459609 --- /dev/null +++ b/app/assets/javascripts/crm/contacts/components/graphql/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/contacts/components/graphql/crm_contact_fields.fragment.graphql b/app/assets/javascripts/crm/contacts/components/graphql/crm_contact_fields.fragment.graphql new file mode 100644 index 00000000000..cef4083b446 --- /dev/null +++ b/app/assets/javascripts/crm/contacts/components/graphql/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/contacts/components/graphql/get_group_contacts.query.graphql b/app/assets/javascripts/crm/contacts/components/graphql/get_group_contacts.query.graphql new file mode 100644 index 00000000000..2a8150e42e3 --- /dev/null +++ b/app/assets/javascripts/crm/contacts/components/graphql/get_group_contacts.query.graphql @@ -0,0 +1,13 @@ +#import "./crm_contact_fields.fragment.graphql" + +query contacts($groupFullPath: ID!) { + group(fullPath: $groupFullPath) { + __typename + id + contacts { + nodes { + ...ContactFragment + } + } + } +} diff --git a/app/assets/javascripts/crm/contacts/components/graphql/update_contact.mutation.graphql b/app/assets/javascripts/crm/contacts/components/graphql/update_contact.mutation.graphql new file mode 100644 index 00000000000..f55f6a10e0a --- /dev/null +++ b/app/assets/javascripts/crm/contacts/components/graphql/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/contacts/routes.js b/app/assets/javascripts/crm/contacts/routes.js new file mode 100644 index 00000000000..18768e1c775 --- /dev/null +++ b/app/assets/javascripts/crm/contacts/routes.js @@ -0,0 +1,20 @@ +import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from '../constants'; +import ContactFormWrapper from './components/contact_form_wrapper.vue'; + +export default [ + { + name: INDEX_ROUTE_NAME, + path: '/', + }, + { + name: NEW_ROUTE_NAME, + path: '/new', + component: ContactFormWrapper, + }, + { + name: EDIT_ROUTE_NAME, + path: '/:id/edit', + component: ContactFormWrapper, + props: { isEditMode: true }, + }, +]; |