diff --git a/app/assets/javascripts/crm/contacts/bundle.js b/app/assets/javascripts/crm/contacts/bundle.js index fe62b7cfbe339b702d73593240801f8081112bd1..a7a3b72e87c0e3b55036cd1d4809610a75fc4253 100644 --- a/app/assets/javascripts/crm/contacts/bundle.js +++ b/app/assets/javascripts/crm/contacts/bundle.js @@ -26,7 +26,9 @@ export default () => { basePath, groupFullPath, groupIssuesPath, + groupOrganizationsPath, canAdminCrmContact, + canReadCrmOrganization, groupId, textQuery, } = el.dataset; @@ -44,7 +46,9 @@ export default () => { provide: { groupFullPath, groupIssuesPath, + groupOrganizationsPath, canAdminCrmContact: parseBoolean(canAdminCrmContact), + canReadCrmOrganization: parseBoolean(canReadCrmOrganization), groupId, textQuery, }, diff --git a/app/assets/javascripts/crm/contacts/components/contacts_root.vue b/app/assets/javascripts/crm/contacts/components/contacts_root.vue index 90a8701cea6946f84a82c155c56c62ac3c608155..1cfb5025ab9e6552e8cf896371aef59f2367f346 100644 --- a/app/assets/javascripts/crm/contacts/components/contacts_root.vue +++ b/app/assets/javascripts/crm/contacts/components/contacts_root.vue @@ -22,7 +22,14 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - inject: ['canAdminCrmContact', 'groupFullPath', 'groupIssuesPath', 'textQuery'], + inject: [ + 'canAdminCrmContact', + 'canReadCrmOrganization', + 'groupFullPath', + 'groupIssuesPath', + 'groupOrganizationsPath', + 'textQuery', + ], data() { return { contacts: { list: [] }, @@ -147,6 +154,7 @@ export default { title: s__('Crm|Customer relations contacts'), newContact: s__('Crm|New contact'), errorMsg: __('Something went wrong. Please try again.'), + organizations: s__('Crm|Organizations'), }, EDIT_ROUTE_NAME, NEW_ROUTE_NAME, @@ -191,11 +199,20 @@ export default { @error-alert-dismissed="errorAlertDismissed" > <template #header-actions> - <router-link v-if="canAdminCrmContact" :to="{ name: $options.NEW_ROUTE_NAME }"> - <gl-button class="gl-my-3 gl-mr-5" variant="confirm" data-testid="new-contact-button"> - {{ $options.i18n.newContact }} - </gl-button> - </router-link> + <div class="gl-display-flex gl-align-items-center gl-justify-content-end gl-my-3 gl-mr-5"> + <a + v-if="canReadCrmOrganization" + :href="groupOrganizationsPath" + class="gl-mr-3" + data-testid="organizations-link" + >{{ $options.i18n.organizations }}</a + > + <router-link v-if="canAdminCrmContact" :to="{ name: $options.NEW_ROUTE_NAME }"> + <gl-button variant="confirm" data-testid="new-contact-button"> + {{ $options.i18n.newContact }} + </gl-button> + </router-link> + </div> </template> <template #title> diff --git a/app/assets/javascripts/crm/organizations/bundle.js b/app/assets/javascripts/crm/organizations/bundle.js index 5897810a38490bf2572d792a00c0bb744a244e01..84ed9229f6f6acc528b2848c8eed649699c06c0d 100644 --- a/app/assets/javascripts/crm/organizations/bundle.js +++ b/app/assets/javascripts/crm/organizations/bundle.js @@ -25,6 +25,8 @@ export default () => { const { basePath, canAdminCrmOrganization, + canReadCrmContact, + groupContactsPath, groupFullPath, groupId, groupIssuesPath, @@ -43,6 +45,8 @@ export default () => { apolloProvider, provide: { canAdminCrmOrganization: parseBoolean(canAdminCrmOrganization), + canReadCrmContact: parseBoolean(canReadCrmContact), + groupContactsPath, groupFullPath, groupId, groupIssuesPath, diff --git a/app/assets/javascripts/crm/organizations/components/organizations_root.vue b/app/assets/javascripts/crm/organizations/components/organizations_root.vue index 2fdb366c6d264bfd157b465df192b6b8086fb2aa..11c34738c33f3c8794dbee151acdfd50ba88ba76 100644 --- a/app/assets/javascripts/crm/organizations/components/organizations_root.vue +++ b/app/assets/javascripts/crm/organizations/components/organizations_root.vue @@ -22,7 +22,14 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath', 'textQuery'], + inject: [ + 'canAdminCrmOrganization', + 'canReadCrmContact', + 'groupContactsPath', + 'groupFullPath', + 'groupIssuesPath', + 'textQuery', + ], data() { return { organizations: { list: [] }, @@ -138,6 +145,7 @@ export default { title: s__('Crm|Customer relations organizations'), newOrganization: s__('Crm|New organization'), errorMsg: __('Something went wrong. Please try again.'), + contacts: s__('Crm|Contacts'), }, EDIT_ROUTE_NAME, NEW_ROUTE_NAME, @@ -182,15 +190,20 @@ export default { @error-alert-dismissed="errorAlertDismissed" > <template #header-actions> - <router-link v-if="canAdminCrmOrganization" :to="{ name: $options.NEW_ROUTE_NAME }"> - <gl-button - class="gl-my-3 gl-mr-5" - variant="confirm" - data-testid="new-organization-button" + <div class="gl-display-flex gl-align-items-center gl-justify-content-end gl-my-3 gl-mr-5"> + <a + v-if="canReadCrmContact" + :href="groupContactsPath" + class="gl-mr-3" + data-testid="contacts-link" + >{{ $options.i18n.contacts }}</a > - {{ $options.i18n.newOrganization }} - </gl-button> - </router-link> + <router-link v-if="canAdminCrmOrganization" :to="{ name: $options.NEW_ROUTE_NAME }"> + <gl-button variant="confirm" data-testid="new-organization-button"> + {{ $options.i18n.newOrganization }} + </gl-button> + </router-link> + </div> </template> <template #title> diff --git a/app/views/groups/crm/contacts/index.html.haml b/app/views/groups/crm/contacts/index.html.haml index 27f18ac1c57b5bc65bc4880c712368eb33f97a4e..f1e7c2f9818363209c89a95e69e1b67e30b7830a 100644 --- a/app/views/groups/crm/contacts/index.html.haml +++ b/app/views/groups/crm/contacts/index.html.haml @@ -1,8 +1,17 @@ - breadcrumb_title _('Customer relations contacts') - page_title _('Customer relations contacts') - @content_wrapper_class = "gl-relative" +- add_page_specific_style 'page_bundles/incident_management_list' = content_for :after_content do #js-crm-form-portal -#js-crm-contacts-app{ data: { group_full_path: @group.full_path, group_issues_path: issues_group_path(@group), group_id: @group.id, can_admin_crm_contact: can?(current_user, :admin_crm_contact, @group).to_s, base_path: group_crm_contacts_path(@group), text_query: params[:search] } } +#js-crm-contacts-app{ data: { + group_full_path: @group.full_path, + group_issues_path: issues_group_path(@group), + group_id: @group.id, + group_organizations_path: group_crm_organizations_path(@group), + can_admin_crm_contact: can?(current_user, :admin_crm_contact, @group).to_s, + can_read_crm_organization: can?(current_user, :read_crm_organization, @group).to_s, + base_path: group_crm_contacts_path(@group), + text_query: params[:search] } } diff --git a/app/views/groups/crm/organizations/index.html.haml b/app/views/groups/crm/organizations/index.html.haml index f7702889eefacf312ff60b1bf049f57ab959e2e9..7497f318b352da1ac76b6e56a6bf171402b276c6 100644 --- a/app/views/groups/crm/organizations/index.html.haml +++ b/app/views/groups/crm/organizations/index.html.haml @@ -1,8 +1,17 @@ - breadcrumb_title _('Customer relations organizations') - page_title _('Customer relations organizations') - @content_wrapper_class = "gl-relative" +- add_page_specific_style 'page_bundles/incident_management_list' = content_for :after_content do #js-crm-form-portal -#js-crm-organizations-app{ data: { base_path: group_crm_organizations_path(@group), can_admin_crm_organization: can?(current_user, :admin_crm_organization, @group).to_s, group_full_path: @group.full_path, group_id: @group.id, group_issues_path: issues_group_path(@group), text_query: params[:search] } } +#js-crm-organizations-app{ data: { + base_path: group_crm_organizations_path(@group), + can_admin_crm_organization: can?(current_user, :admin_crm_organization, @group).to_s, + can_read_crm_contact: can?(current_user, :read_crm_contact, @group).to_s, + group_contacts_path: group_crm_contacts_path(@group), + group_full_path: @group.full_path, + group_id: @group.id, + group_issues_path: issues_group_path(@group), + text_query: params[:search] } } diff --git a/doc/user/crm/index.md b/doc/user/crm/index.md index f12f4bb539f78a6958d9044d8e283881ec1953dd..d552b540c89d899c4599ba3e889a5efc3dee8078 100644 --- a/doc/user/crm/index.md +++ b/doc/user/crm/index.md @@ -57,7 +57,7 @@ Prerequisites: To view a group's contacts: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer contacts**. +1. Select **Plan > Customer relations**.  @@ -70,7 +70,7 @@ Prerequisites: To create a contact: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer contacts**. +1. Select **Plan > Customer relations**. 1. Select **New contact**. 1. Complete all required fields. 1. Select **Create new contact**. @@ -87,7 +87,7 @@ Prerequisites: To edit an existing contact: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer contacts**. +1. Select **Plan > Customer relations**. 1. Next to the contact you wish to edit, select **Edit** (**{pencil}**). 1. Edit the required fields. 1. Select **Save changes**. @@ -105,7 +105,7 @@ Each contact can be in one of two states: To change the state of a contact: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer contacts**. +1. Select **Plan > Customer relations**. 1. Next to the contact you wish to edit, select **Edit** (**{pencil}**). 1. Select or clear the **Active** checkbox. 1. Select **Save changes**. @@ -121,7 +121,8 @@ Prerequisites: To view a group's organizations: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer organizations**. +1. Select **Plan > Customer relations**. +1. In the upper right, select **Organizations**.  @@ -134,7 +135,8 @@ Prerequisites: To create an organization: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer organizations**. +1. Select **Plan > Customer relations**. +1. In the upper right, select **Organizations**. 1. Select **New organization**. 1. Complete all required fields. 1. Select **Create new organization**. @@ -151,7 +153,8 @@ Prerequisites: To edit an existing organization: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer organizations**. +1. Select **Plan > Customer relations**. +1. In the upper right, select **Organizations**. 1. Next to the organization you wish to edit, select **Edit** (**{pencil}**). 1. Edit the required fields. 1. Select **Save changes**. @@ -173,7 +176,7 @@ Prerequisites: To view a contact's issues, select a contact from the issue sidebar, or: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer contacts**. +1. Select **Plan > Customer relations**. 1. Next to the contact whose issues you wish to view, select **View issues** (**{issues}**). ### View issues linked to an organization @@ -185,7 +188,8 @@ Prerequisites: To view an organization's issues: 1. On the left sidebar, select **Search or go to** and find your group. -1. Select **Plan > Customer organizations**. +1. Select **Plan > Customer relations**. +1. In the upper right, select **Organizations**. 1. Next to the organization whose issues you wish to view, select **View issues** (**{issues}**). ### View contacts linked to an issue diff --git a/lib/sidebars/groups/menus/customer_relations_menu.rb b/lib/sidebars/groups/menus/customer_relations_menu.rb index ac25cb312cd4eb344583b6202bd33f6e49636450..a0d89248d815b48f38deb33af2a7edf1848d9c55 100644 --- a/lib/sidebars/groups/menus/customer_relations_menu.rb +++ b/lib/sidebars/groups/menus/customer_relations_menu.rb @@ -7,7 +7,6 @@ class CustomerRelationsMenu < ::Sidebars::Menu override :configure_menu_items def configure_menu_items add_item(contacts_menu_item) if can_read_contact? - add_item(organizations_menu_item) if can_read_organization? true end @@ -26,7 +25,7 @@ def sprite_icon def render? return false unless context.group.root? - can_read_contact? || can_read_organization? + can_read_contact? end override :serialize_as_menu_item_args @@ -38,31 +37,17 @@ def serialize_as_menu_item_args def contacts_menu_item ::Sidebars::MenuItem.new( - title: context.is_super_sidebar ? _('Customer contacts') : _('Contacts'), + title: context.is_super_sidebar ? _('Customer relations') : _('Contacts'), link: group_crm_contacts_path(context.group), super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::PlanMenu, - active_routes: { controller: 'groups/crm/contacts' }, + active_routes: { controller: %w[groups/crm/contacts groups/crm/organizations] }, item_id: :crm_contacts ) end - def organizations_menu_item - ::Sidebars::MenuItem.new( - title: context.is_super_sidebar ? _('Customer organizations') : _('Organizations'), - link: group_crm_organizations_path(context.group), - super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::PlanMenu, - active_routes: { controller: 'groups/crm/organizations' }, - item_id: :crm_organizations - ) - end - def can_read_contact? can?(context.current_user, :read_crm_contact, context.group) end - - def can_read_organization? - can?(context.current_user, :read_crm_organization, context.group) - end end end end diff --git a/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb b/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb index bf122f930a2cda3b8af08a734d6a45886e1872ea..7bfe237c32a3d4123025d82582e7fcce4e386634 100644 --- a/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb +++ b/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb @@ -25,8 +25,7 @@ def configure_menu_items :milestones, :iterations, :group_wiki, - :crm_contacts, - :crm_organizations + :crm_contacts ].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) } end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c93aafa99e7be74523d7f02bd4dee430636db551..dcb4c4176927acbde6637f4f0efc330a7034fca7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -15027,6 +15027,9 @@ msgstr "" msgid "Crm|Contact has been updated." msgstr "" +msgid "Crm|Contacts" +msgstr "" + msgid "Crm|Customer relations contacts" msgstr "" @@ -15066,6 +15069,9 @@ msgstr "" msgid "Crm|Organization has been updated." msgstr "" +msgid "Crm|Organizations" +msgstr "" + msgid "Cron time zone" msgstr "" @@ -15195,15 +15201,9 @@ msgstr "" msgid "Custom range" msgstr "" -msgid "Customer contacts" -msgstr "" - msgid "Customer experience improvement and third-party offers" msgstr "" -msgid "Customer organizations" -msgstr "" - msgid "Customer relations" msgstr "" diff --git a/spec/frontend/crm/contacts_root_spec.js b/spec/frontend/crm/contacts_root_spec.js index d6092e859171ec59a24d8969bb715955da387658..72281afe8b9aa0c23551246cfc05c41e1033f680 100644 --- a/spec/frontend/crm/contacts_root_spec.js +++ b/spec/frontend/crm/contacts_root_spec.js @@ -16,6 +16,7 @@ describe('Customer relations contacts root app', () => { let wrapper; let fakeApollo; + const findOrganizationsLink = () => wrapper.findByTestId('organizations-link'); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findNewContactButton = () => wrapper.findByTestId('new-contact-button'); const findTable = () => wrapper.findComponent(PaginatedTableWithSearchAndTabs); @@ -26,6 +27,7 @@ describe('Customer relations contacts root app', () => { queryHandler = successQueryHandler, countQueryHandler = successCountQueryHandler, canAdminCrmContact = true, + canReadCrmOrganization = true, textQuery = null, } = {}) => { fakeApollo = createMockApollo([ @@ -37,7 +39,9 @@ describe('Customer relations contacts root app', () => { groupFullPath: 'flightjs', groupId: 26, groupIssuesPath: '/issues', + groupOrganizationsPath: '/organizations', canAdminCrmContact, + canReadCrmOrganization, textQuery, }, apolloProvider: fakeApollo, @@ -75,6 +79,20 @@ describe('Customer relations contacts root app', () => { expect(findLoadingIcon().exists()).toBe(true); }); + describe('organizations link', () => { + it('renders when canReadCrmOrganization is true', () => { + mountComponent(); + + expect(findOrganizationsLink().attributes('href')).toBe('/organizations'); + }); + + it('does not render when canReadCrmOrganization is false', () => { + mountComponent({ canReadCrmOrganization: false }); + + expect(findOrganizationsLink().exists()).toBe(false); + }); + }); + describe('new contact button', () => { it('should exist when user has permission', () => { mountComponent(); diff --git a/spec/frontend/crm/organizations_root_spec.js b/spec/frontend/crm/organizations_root_spec.js index b6141bd983530de75aba134f4578aad20cad321e..3ae9503f106ebe102f167de1493c33bf00d26f7d 100644 --- a/spec/frontend/crm/organizations_root_spec.js +++ b/spec/frontend/crm/organizations_root_spec.js @@ -19,6 +19,7 @@ describe('Customer relations organizations root app', () => { let wrapper; let fakeApollo; + const findContactsLink = () => wrapper.findByTestId('contacts-link'); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findNewOrganizationButton = () => wrapper.findByTestId('new-organization-button'); const findTable = () => wrapper.findComponent(PaginatedTableWithSearchAndTabs); @@ -31,6 +32,7 @@ describe('Customer relations organizations root app', () => { queryHandler = successQueryHandler, countQueryHandler = successCountQueryHandler, canAdminCrmOrganization = true, + canReadCrmContact = true, textQuery = null, } = {}) => { fakeApollo = createMockApollo([ @@ -40,6 +42,8 @@ describe('Customer relations organizations root app', () => { wrapper = shallowMountExtended(OrganizationsRoot, { provide: { canAdminCrmOrganization, + canReadCrmContact, + groupContactsPath: '/contacts', groupFullPath: 'flightjs', groupIssuesPath: '/issues', textQuery, @@ -79,6 +83,20 @@ describe('Customer relations organizations root app', () => { expect(findLoadingIcon().exists()).toBe(true); }); + describe('contacts link', () => { + it('renders when canReadContact is true', () => { + mountComponent(); + + expect(findContactsLink().attributes('href')).toBe('/contacts'); + }); + + it('does not render when canReadContact is false', () => { + mountComponent({ canReadCrmContact: false }); + + expect(findContactsLink().exists()).toBe(false); + }); + }); + describe('new organization button', () => { it('should exist when user has permission', () => { mountComponent(); diff --git a/spec/lib/sidebars/groups/super_sidebar_menus/plan_menu_spec.rb b/spec/lib/sidebars/groups/super_sidebar_menus/plan_menu_spec.rb index 1ac2cf87236e6c554e3113300aba4d0474b5c7c1..d52cb79dc2f6b9cbf30a266121d0f23aa7c6e9dd 100644 --- a/spec/lib/sidebars/groups/super_sidebar_menus/plan_menu_spec.rb +++ b/spec/lib/sidebars/groups/super_sidebar_menus/plan_menu_spec.rb @@ -23,8 +23,7 @@ :milestones, :iterations, :group_wiki, - :crm_contacts, - :crm_organizations + :crm_contacts ]) end end diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb index e4b30df063fa43d63e8a387cb336f84b672d586b..4f405adb5bebb4dfb569483c0a4398705794bab5 100644 --- a/spec/support/helpers/navbar_structure_helper.rb +++ b/spec/support/helpers/navbar_structure_helper.rb @@ -59,12 +59,7 @@ def insert_customer_relations_nav(after) insert_after_sub_nav_item( after, within: _('Plan'), - new_sub_nav_item_name: _("Customer contacts") - ) - insert_after_sub_nav_item( - _("Customer contacts"), - within: _('Plan'), - new_sub_nav_item_name: _("Customer organizations") + new_sub_nav_item_name: _("Customer relations") ) end