Merge pull request #33163 from overleaf/ii-share-modal-give-feedback

[web] Share modal give feedback link

GitOrigin-RevId: 5e83dec6c6b97c172b7600d8ded285db49178a64
This commit is contained in:
ilkin-overleaf
2026-05-06 16:49:21 +03:00
committed by Copybot
parent de9b07f0b9
commit faec27d7b0
6 changed files with 107 additions and 3 deletions

View File

@@ -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,
},

View File

@@ -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 (
<OLButton
variant="link"
size="sm"
href={link}
target="_blank"
rel="noopener noreferrer"
className="fw-bold"
>
{t('give_feedback')}
</OLButton>
)
}

View File

@@ -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 (
<OLModal show={show} onHide={cancel} animation={animation}>
@@ -71,6 +71,7 @@ export default function ShareProjectModalContent({
: t('share_project')}
</OLModalTitle>
)}
{isSharingUpdatesEnabled && isProjectOwner && <GiveFeedbackLink />}
</div>
</OLModalHeader>

View File

@@ -1053,6 +1053,15 @@ describe('<ShareProjectModal/>', function () {
})
})
it('does not show the "Give feedback" link when the "sharing-updates" feature flag is disabled', async function () {
renderWithEditorContext(
<ShareProjectModal {...modalProps} />,
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('<ShareProjectModal/>', function () {
await screen.findByText('Invitation(s) sent.')
})
it('shows the "Give feedback" link for the project owner', async function () {
renderWithEditorContext(
<ShareProjectModal {...modalProps} />,
createContextProps()
)
await screen.findByRole('link', { name: 'Give feedback' })
})
it('does not show the "Give feedback" link for non-owners', async function () {
renderWithEditorContext(<ShareProjectModal {...modalProps} />, {
...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(<ShareProjectModal {...modalProps} />, {
...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(<ShareProjectModal {...modalProps} />, {
...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'
)
})
})
})
})

View File

@@ -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']

View File

@@ -62,6 +62,7 @@ export type User = {
planName?: string
isAnnualPlan?: boolean
isMemberOfGroupSubscription?: boolean
isProfessionalGroupPlan?: boolean
hasInstitutionLicence?: boolean
}