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>2021-12-03 15:10:23 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-03 15:10:23 +0300
commit5f0d27d131aced1a53e8cbc7db023d9f947f8a1a (patch)
tree7007c07fc37c95638f3e71c1902dcd055db1d8ca /app/assets/javascripts/crm
parentcc8ea69201e2e4d020018c43efeb993c44cd8a71 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/crm')
-rw-r--r--app/assets/javascripts/crm/components/contacts_root.vue67
-rw-r--r--app/assets/javascripts/crm/components/new_contact_form.vue140
-rw-r--r--app/assets/javascripts/crm/components/queries/create_contact.mutation.graphql10
-rw-r--r--app/assets/javascripts/crm/components/queries/crm_contact_fields.fragment.graphql14
-rw-r--r--app/assets/javascripts/crm/components/queries/get_group_contacts.query.graphql15
-rw-r--r--app/assets/javascripts/crm/contacts_bundle.js20
6 files changed, 242 insertions, 24 deletions
diff --git a/app/assets/javascripts/crm/components/contacts_root.vue b/app/assets/javascripts/crm/components/contacts_root.vue
index 97220a3409d..0242bdab541 100644
--- a/app/assets/javascripts/crm/components/contacts_root.vue
+++ b/app/assets/javascripts/crm/components/contacts_root.vue
@@ -1,22 +1,28 @@
<script>
-import { GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
-import createFlash from '~/flash';
+import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import getGroupContactsQuery from './queries/get_group_contacts.query.graphql';
+import NewContactForm from './new_contact_form.vue';
export default {
components: {
+ GlAlert,
GlButton,
GlLoadingIcon,
GlTable,
+ NewContactForm,
},
directives: {
GlTooltip: GlTooltipDirective,
},
- inject: ['groupFullPath', 'groupIssuesPath'],
+ inject: ['groupFullPath', 'groupIssuesPath', 'canAdminCrmContact'],
data() {
- return { contacts: [] };
+ return {
+ contacts: [],
+ error: false,
+ errorMessages: [],
+ };
},
apollo: {
contacts: {
@@ -31,12 +37,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;
},
},
},
@@ -44,12 +46,31 @@ export default {
isLoading() {
return this.$apollo.queries.contacts.loading;
},
+ showNewForm() {
+ return this.$route.path.startsWith('/new');
+ },
},
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({ path: '/new' });
+ },
+ hideNewForm() {
+ this.$router.replace({ path: '/' });
+ },
+ handleError(errors) {
+ this.error = true;
+ if (errors) this.errorMessages = errors;
+ },
+ dismissError() {
+ this.error = false;
+ this.errorMessages = [];
+ },
},
fields: [
{ key: 'firstName', sortable: true },
@@ -75,15 +96,41 @@ export default {
i18n: {
emptyText: s__('Crm|No contacts found'),
issuesButtonLabel: __('View issues'),
+ 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="dismissError">
+ <div v-if="errorMessages.length == 0">{{ $options.i18n.errorText }}</div>
+ <div v-for="(message, index) in errorMessages" :key="index">{{ message }}</div>
+ </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="canAdminCrmContact"
+ variant="confirm"
+ data-testid="new-contact-button"
+ @click="displayNewForm"
+ >
+ {{ $options.i18n.newContact }}
+ </gl-button>
+ </div>
+ </div>
+ <new-contact-form v-if="showNewForm" @close="hideNewForm" @error="handleError" />
<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"
diff --git a/app/assets/javascripts/crm/components/new_contact_form.vue b/app/assets/javascripts/crm/components/new_contact_form.vue
new file mode 100644
index 00000000000..77ff82c5af4
--- /dev/null
+++ b/app/assets/javascripts/crm/components/new_contact_form.vue
@@ -0,0 +1,140 @@
+<script>
+import { GlButton, 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 createContact from './queries/create_contact.mutation.graphql';
+import getGroupContactsQuery from './queries/get_group_contacts.query.graphql';
+
+export default {
+ components: {
+ GlButton,
+ GlFormGroup,
+ GlFormInput,
+ },
+ inject: ['groupFullPath', 'groupId'],
+ data() {
+ return {
+ firstName: '',
+ lastName: '',
+ phone: '',
+ email: '',
+ description: '',
+ submitting: false,
+ };
+ },
+ computed: {
+ invalid() {
+ return this.firstName === '' || this.lastName === '' || this.email === '';
+ },
+ },
+ methods: {
+ save() {
+ this.submitting = true;
+ return this.$apollo
+ .mutate({
+ mutation: createContact,
+ variables: {
+ input: {
+ groupId: convertToGraphQLId(TYPE_GROUP, this.groupId),
+ firstName: this.firstName,
+ lastName: this.lastName,
+ phone: this.phone,
+ email: this.email,
+ description: this.description,
+ },
+ },
+ update: this.updateCache,
+ })
+ .then(({ data }) => {
+ if (data.customerRelationsContactCreate.errors.length === 0) this.close();
+
+ this.submitting = false;
+ })
+ .catch(() => {
+ this.error();
+ this.submitting = false;
+ });
+ },
+ close() {
+ this.$emit('close');
+ },
+ error(errors = null) {
+ this.$emit('error', errors);
+ },
+ updateCache(store, { data: { customerRelationsContactCreate } }) {
+ if (customerRelationsContactCreate.errors.length > 0) {
+ this.error(customerRelationsContactCreate.errors);
+ return;
+ }
+
+ const variables = {
+ groupFullPath: this.groupFullPath,
+ };
+ const sourceData = store.readQuery({
+ query: getGroupContactsQuery,
+ variables,
+ });
+
+ const data = produce(sourceData, (draftState) => {
+ draftState.group.contacts.nodes = [
+ ...sourceData.group.contacts.nodes,
+ customerRelationsContactCreate.contact,
+ ];
+ });
+
+ store.writeQuery({
+ query: getGroupContactsQuery,
+ variables,
+ data,
+ });
+ },
+ },
+ i18n: {
+ buttonLabel: s__('Crm|Create new contact'),
+ 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)'),
+ },
+};
+</script>
+
+<template>
+ <div class="col-md-4">
+ <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>
+ <div class="form-actions">
+ <gl-button
+ variant="confirm"
+ :disabled="invalid"
+ :loading="submitting"
+ data-testid="create-new-contact-button"
+ type="submit"
+ >{{ $options.i18n.buttonLabel }}</gl-button
+ >
+ <gl-button data-testid="cancel-button" @click="close">
+ {{ $options.i18n.cancel }}
+ </gl-button>
+ </div>
+ </form>
+ <div class="gl-pb-5"></div>
+ </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/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/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/contacts_bundle.js b/app/assets/javascripts/crm/contacts_bundle.js
index b0edd0107b6..6ddc53840cc 100644
--- a/app/assets/javascripts/crm/contacts_bundle.js
+++ b/app/assets/javascripts/crm/contacts_bundle.js
@@ -1,9 +1,11 @@
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';
Vue.use(VueApollo);
+Vue.use(VueRouter);
export default () => {
const el = document.getElementById('js-crm-contacts-app');
@@ -16,12 +18,26 @@ export default () => {
return false;
}
- const { groupFullPath, groupIssuesPath } = el.dataset;
+ const { basePath, groupFullPath, groupIssuesPath, canAdminCrmContact, groupId } = el.dataset;
+
+ const router = new VueRouter({
+ base: basePath,
+ mode: 'history',
+ routes: [
+ {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ name: 'Contacts List',
+ path: '/',
+ component: CrmContactsRoot,
+ },
+ ],
+ });
return new Vue({
el,
+ router,
apolloProvider,
- provide: { groupFullPath, groupIssuesPath },
+ provide: { groupFullPath, groupIssuesPath, canAdminCrmContact, groupId },
render(createElement) {
return createElement(CrmContactsRoot);
},