diff --git a/app/assets/javascripts/deployments/components/details_feedback.vue b/app/assets/javascripts/deployments/components/details_feedback.vue new file mode 100644 index 0000000000000000000000000000000000000000..ff98457e1fa6e24af7908cdc3c01480ebee0b5d4 --- /dev/null +++ b/app/assets/javascripts/deployments/components/details_feedback.vue @@ -0,0 +1,35 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue'; +import { s__ } from '~/locale'; + +export default { + components: { + GlAlert, + UserCalloutDismisser, + }, + i18n: { + header: s__('Deployment|What would you like to see here?'), + body: s__( + "Deployment|We know this page is a bit empty at the moment, but we're working hard to bring you a great experience! In the meantime, we'd love to hear your feedback.", + ), + action: s__('Deployment|Leave feedback'), + }, + FEEDBACK_ISSUE_LINK: 'https://gitlab.com/gitlab-org/gitlab/-/issues/450700', +}; +</script> +<template> + <user-callout-dismisser feature-name="deployment_details_feedback"> + <template #default="{ dismiss, shouldShowCallout }"> + <gl-alert + v-if="shouldShowCallout" + :title="$options.i18n.header" + :primary-button-text="$options.i18n.action" + :primary-button-link="$options.FEEDBACK_ISSUE_LINK" + @dismiss="dismiss" + > + {{ $options.i18n.body }} + </gl-alert> + </template> + </user-callout-dismisser> +</template> diff --git a/app/assets/javascripts/deployments/components/show_deployment.vue b/app/assets/javascripts/deployments/components/show_deployment.vue index 6e62925ced1330d50a6934a6a972e4447e84e479..b5abfe241e54aa8f9cbda36f10478190ec13f6ad 100644 --- a/app/assets/javascripts/deployments/components/show_deployment.vue +++ b/app/assets/javascripts/deployments/components/show_deployment.vue @@ -8,6 +8,7 @@ import environmentQuery from '../graphql/queries/environment.query.graphql'; import DeploymentHeader from './deployment_header.vue'; import DeploymentAside from './deployment_aside.vue'; import DeploymentDeployBlock from './deployment_deploy_block.vue'; +import DetailsFeedback from './details_feedback.vue'; const DEPLOYMENT_QUERY_POLLING_INTERVAL = 3000; @@ -18,6 +19,7 @@ export default { DeploymentHeader, DeploymentAside, DeploymentDeployBlock, + DetailsFeedback, DeploymentApprovals: () => import('ee_component/deployments/components/deployment_approvals.vue'), DeploymentTimeline: () => import('ee_component/deployments/components/deployment_timeline.vue'), @@ -99,6 +101,7 @@ export default { :environment="environment" :loading="$apollo.queries.deployment.loading" /> + <details-feedback class="gl-mt-6 gl-w-90p" /> <deployment-approvals v-if="hasApprovalSummary" :approval-summary="deployment.approvalSummary" diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb index 5c472d4c344f06befad4c1935b6d0a90bac5f9bf..140339e1bb09f02087a56240d899666682358cc9 100644 --- a/app/models/users/callout.rb +++ b/app/models/users/callout.rb @@ -85,7 +85,8 @@ class Callout < MainClusterwide::ApplicationRecord joining_a_project_alert: 83, # EE-only transition_to_jihu_callout: 84, summarize_code_changes: 85, # EE-only - duo_pro_trial_alert: 86 # EE-only + duo_pro_trial_alert: 86, # EE-only + deployment_details_feedback: 87 } validates :feature_name, diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index cadafab80b65d7ace21975d118c4d69c50655cc8..efd7353e506ab9f2cf4fcee853900bf940aed1a4 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -33342,6 +33342,7 @@ Name of the feature that the callout is for. | <a id="usercalloutfeaturenameenumci_deprecation_warning_for_types_keyword"></a>`CI_DEPRECATION_WARNING_FOR_TYPES_KEYWORD` | Callout feature name for ci_deprecation_warning_for_types_keyword. | | <a id="usercalloutfeaturenameenumcloud_licensing_subscription_activation_banner"></a>`CLOUD_LICENSING_SUBSCRIPTION_ACTIVATION_BANNER` | Callout feature name for cloud_licensing_subscription_activation_banner. | | <a id="usercalloutfeaturenameenumcluster_security_warning"></a>`CLUSTER_SECURITY_WARNING` | Callout feature name for cluster_security_warning. | +| <a id="usercalloutfeaturenameenumdeployment_details_feedback"></a>`DEPLOYMENT_DETAILS_FEEDBACK` | Callout feature name for deployment_details_feedback. | | <a id="usercalloutfeaturenameenumduo_chat_callout"></a>`DUO_CHAT_CALLOUT` | Callout feature name for duo_chat_callout. | | <a id="usercalloutfeaturenameenumduo_pro_trial_alert"></a>`DUO_PRO_TRIAL_ALERT` | Callout feature name for duo_pro_trial_alert. | | <a id="usercalloutfeaturenameenumfeature_flags_new_version"></a>`FEATURE_FLAGS_NEW_VERSION` | Callout feature name for feature_flags_new_version. | diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a359e0f6191fec222bfd0763bd3974a2d3822196..644775f4d070a33c82b32adc37869991646c04a7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17785,6 +17785,9 @@ msgstr "" msgid "Deployment|Latest Deployed" msgstr "" +msgid "Deployment|Leave feedback" +msgstr "" + msgid "Deployment|Needs Approval" msgstr "" @@ -17850,6 +17853,12 @@ msgstr "" msgid "Deployment|Waiting to be deployed." msgstr "" +msgid "Deployment|We know this page is a bit empty at the moment, but we're working hard to bring you a great experience! In the meantime, we'd love to hear your feedback." +msgstr "" + +msgid "Deployment|What would you like to see here?" +msgstr "" + msgid "Deprecated API rate limits" msgstr "" diff --git a/spec/frontend/deployments/components/details_feedback_spec.js b/spec/frontend/deployments/components/details_feedback_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..6b4627ab86fe73ee5a41fe5b8ddcfb2c3772192a --- /dev/null +++ b/spec/frontend/deployments/components/details_feedback_spec.js @@ -0,0 +1,53 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlAlert } from '@gitlab/ui'; +import DetailsFeedback from '~/deployments/components/details_feedback.vue'; +import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser'; + +jest.mock('~/sentry/sentry_browser_wrapper'); +jest.mock('~/graphql_shared/utils'); + +describe('~/deployments/components/details_feedback.vue', () => { + let wrapper; + let dismiss; + let dismisserComponent; + + const createComponent = ({ shouldShowCallout = true } = {}) => { + dismiss = jest.fn(); + dismisserComponent = makeMockUserCalloutDismisser({ + dismiss, + shouldShowCallout, + }); + wrapper = shallowMount(DetailsFeedback, { + stubs: { + UserCalloutDismisser: dismisserComponent, + }, + }); + }; + + const findAlert = () => wrapper.findComponent(GlAlert); + + it('shows an alert', () => { + createComponent(); + expect(findAlert().exists()).toBe(true); + }); + + it('calls dismiss when the alert is dismissed', () => { + createComponent(); + findAlert().vm.$emit('dismiss'); + expect(dismiss).toHaveBeenCalled(); + }); + + it('links to the feedback issue', () => { + createComponent(); + expect(findAlert().props()).toMatchObject({ + title: 'What would you like to see here?', + primaryButtonText: 'Leave feedback', + primaryButtonLink: 'https://gitlab.com/gitlab-org/gitlab/-/issues/450700', + }); + }); + + it('hides the alert if already dismissed', () => { + createComponent({ shouldShowCallout: false }); + expect(findAlert().exists()).toBe(false); + }); +}); diff --git a/spec/frontend/deployments/components/show_deployment_spec.js b/spec/frontend/deployments/components/show_deployment_spec.js index f76967a5027b3a0a9952586dc5b8f77e57578a26..bd92e620ca8a8d64d5d7a673ea632abb60570d6f 100644 --- a/spec/frontend/deployments/components/show_deployment_spec.js +++ b/spec/frontend/deployments/components/show_deployment_spec.js @@ -9,6 +9,7 @@ import { toggleQueryPollingByVisibility } from '~/graphql_shared/utils'; import ShowDeployment from '~/deployments/components/show_deployment.vue'; import DeploymentHeader from '~/deployments/components/deployment_header.vue'; import DeploymentDeployBlock from '~/deployments/components/deployment_deploy_block.vue'; +import DetailsFeedback from '~/deployments/components/details_feedback.vue'; import deploymentQuery from '~/deployments/graphql/queries/deployment.query.graphql'; import environmentQuery from '~/deployments/graphql/queries/environment.query.graphql'; import waitForPromises from 'helpers/wait_for_promises'; @@ -106,6 +107,10 @@ describe('~/deployments/components/show_deployment.vue', () => { }); }); + it('shows an alert asking for feedback', () => { + expect(wrapper.findComponent(DetailsFeedback).exists()).toBe(true); + }); + it('shows the deployment block if the deployment job is manual', () => { expect(wrapper.findComponent(DeploymentDeployBlock).props()).toEqual({ deployment: mockDeploymentFixture.data.project.deployment,