diff --git a/services/web/app/src/Features/Project/ProjectController.mjs b/services/web/app/src/Features/Project/ProjectController.mjs index 75bb479fa1..de991e6297 100644 --- a/services/web/app/src/Features/Project/ProjectController.mjs +++ b/services/web/app/src/Features/Project/ProjectController.mjs @@ -17,6 +17,7 @@ import { User } from '../../models/User.mjs' import SubscriptionLocator from '../Subscription/SubscriptionLocator.mjs' import SubscriptionHelper from '../Subscription/SubscriptionHelper.mjs' import LimitationsManager from '../Subscription/LimitationsManager.mjs' +import { isProfessionalGroupPlan } from '../Subscription/PlansHelper.mjs' import Settings from '@overleaf/settings' import AuthorizationManager from '../Authorization/AuthorizationManager.mjs' import InactiveProjectManager from '../InactiveData/InactiveProjectManager.mjs' @@ -932,6 +933,9 @@ const _ProjectController = { planCode, planName: planDetails?.name, isAnnualPlan: planCode && planDetails?.annual, + isProfessionalGroupPlan: Boolean( + subscription && isProfessionalGroupPlan(subscription) + ), isMemberOfGroupSubscription: userIsMemberOfGroupSubscription, hasInstitutionLicence: userHasInstitutionLicence, }, diff --git a/services/web/frontend/js/features/share-project-modal/components/give-feedback-link.tsx b/services/web/frontend/js/features/share-project-modal/components/give-feedback-link.tsx new file mode 100644 index 0000000000..2199c0b8e0 --- /dev/null +++ b/services/web/frontend/js/features/share-project-modal/components/give-feedback-link.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from 'react-i18next' +import OLButton from '@/shared/components/ol/ol-button' +import getMeta from '@/utils/meta' + +export default function GiveFeedbackLink() { + const { t } = useTranslation() + const isProfessionalGroupPlan = getMeta('ol-user')?.isProfessionalGroupPlan + + const link = isProfessionalGroupPlan + ? 'https://forms.gle/rz1JDMuNajWG4ZY49' + : 'https://forms.gle/WLEjzG4Ayp8zFscM9' + + return ( + + {t('give_feedback')} + + ) +} diff --git a/services/web/frontend/js/features/share-project-modal/components/share-project-modal-content.tsx b/services/web/frontend/js/features/share-project-modal/components/share-project-modal-content.tsx index a45b21f3c4..a26de262d0 100644 --- a/services/web/frontend/js/features/share-project-modal/components/share-project-modal-content.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/share-project-modal-content.tsx @@ -15,6 +15,7 @@ import OLButton from '@/shared/components/ol/ol-button' import OLSpinner from '@/shared/components/ol/ol-spinner' import MaterialIcon from '@/shared/components/material-icon' import ErrorMessage from '@/features/share-project-modal/components/error-message' +import GiveFeedbackLink from '@/features/share-project-modal/components/give-feedback-link' import classNames from 'classnames' import { useFeatureFlag } from '@/shared/context/split-test-context' import { useShareProjectContext } from '@/features/share-project-modal/components/share-project-modal' @@ -49,8 +50,7 @@ export default function ShareProjectModalContent({ const isSharingUpdatesEnabled = useFeatureFlag('sharing-updates') const [isInvitedPeopleScreen, setIsInvitedPeopleScreen] = useState(false) const { successActionMessage } = useShareProjectContext() - - const { isRestrictedTokenMember } = useEditorContext() + const { isRestrictedTokenMember, isProjectOwner } = useEditorContext() return ( @@ -71,6 +71,7 @@ export default function ShareProjectModalContent({ : t('share_project')} )} + {isSharingUpdatesEnabled && isProjectOwner && } diff --git a/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.tsx b/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.tsx index a7671307fb..01146fecea 100644 --- a/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.tsx +++ b/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.tsx @@ -1053,6 +1053,15 @@ describe('', function () { }) }) + it('does not show the "Give feedback" link when the "sharing-updates" feature flag is disabled', async function () { + renderWithEditorContext( + , + createContextProps() + ) + + expect(screen.queryByRole('link', { name: 'Give feedback' })).to.be.null + }) + describe('with "sharing-updates" feature flag', function () { beforeEach(function () { window.metaAttributesCache.set('ol-splitTestVariants', { @@ -1174,5 +1183,64 @@ describe('', function () { await screen.findByText('Invitation(s) sent.') }) + + it('shows the "Give feedback" link for the project owner', async function () { + renderWithEditorContext( + , + createContextProps() + ) + + await screen.findByRole('link', { name: 'Give feedback' }) + }) + + it('does not show the "Give feedback" link for non-owners', async function () { + renderWithEditorContext(, { + ...createContextProps(), + user: { + id: 'non-project-owner', + email: 'non-project-owner@example.com', + }, + }) + + expect(screen.queryByRole('link', { name: 'Give feedback' })).to.be.null + }) + + describe('"Give feedback" link URL based on subscription plan', function () { + it('links to the professional feedback URL when the user has a professional group plan', async function () { + renderWithEditorContext(, { + ...createContextProps(), + user: { + id: USER_ID, + email: USER_EMAIL, + isProfessionalGroupPlan: true, + }, + }) + + const feedbackLink = await screen.findByRole('link', { + name: 'Give feedback', + }) + expect(feedbackLink.getAttribute('href')).to.equal( + 'https://forms.gle/rz1JDMuNajWG4ZY49' + ) + }) + + it('links to the standard feedback URL when the user does not have a professional group plan', async function () { + renderWithEditorContext(, { + ...createContextProps(), + user: { + id: USER_ID, + email: USER_EMAIL, + isProfessionalGroupPlan: false, + }, + }) + + const feedbackLink = await screen.findByRole('link', { + name: 'Give feedback', + }) + expect(feedbackLink.getAttribute('href')).to.equal( + 'https://forms.gle/WLEjzG4Ayp8zFscM9' + ) + }) + }) }) }) diff --git a/services/web/test/frontend/helpers/editor-providers.tsx b/services/web/test/frontend/helpers/editor-providers.tsx index 0330ca63c0..d81e4716e7 100644 --- a/services/web/test/frontend/helpers/editor-providers.tsx +++ b/services/web/test/frontend/helpers/editor-providers.tsx @@ -70,7 +70,12 @@ const defaultUserSettings = { } satisfies UserSettings export type EditorProvidersProps = { - user?: { id: string; email: string; signUpDate?: string } + user?: { + id: string + email: string + signUpDate?: string + isProfessionalGroupPlan?: boolean + } projectId?: string projectName?: string projectOwner?: ProjectMetadata['owner'] diff --git a/services/web/types/user.ts b/services/web/types/user.ts index 071600d33b..ebb9f8eb17 100644 --- a/services/web/types/user.ts +++ b/services/web/types/user.ts @@ -62,6 +62,7 @@ export type User = { planName?: string isAnnualPlan?: boolean isMemberOfGroupSubscription?: boolean + isProfessionalGroupPlan?: boolean hasInstitutionLicence?: boolean }