Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Create alert for paid plans #2841

Merged
merged 29 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5dcfbbe
feat: Add free plan seats alert
RulaKhaled May 6, 2024
ec14c36
feat: Create alerts for paid plan
RulaKhaled May 6, 2024
052a665
fix: Remove repositoryDeprecated from path contents hooks (#2845)
nicholas-codecov May 6, 2024
a50e79d
Update useBranchCoverageMeasurements to use repository instead of rep…
spalmurray-codecov May 7, 2024
b1a3527
feat: Paid/Free plan seats limit banner (#2830)
RulaKhaled May 7, 2024
6c79478
feat: Paid plans activation required banner (#2832)
RulaKhaled May 7, 2024
9d5c1c9
Unlink PR author on pulls table (#2846)
spalmurray-codecov May 7, 2024
8a27276
ref: Use Repository instead of RepositoryDeprecated in useCommitYaml …
spalmurray-codecov May 7, 2024
e0b313b
ref: Convert useCommitErrors to TS and remove repositoryDeprecated (#…
ajay-sentry May 7, 2024
b9982dd
feat: Route to plan if user session + to param == plan (#2837)
ajay-sentry May 7, 2024
e0b7c48
fix: Show banner for private repos only (#2853)
RulaKhaled May 7, 2024
a66c98f
fix: Footer Codecov icon displaying incorrect color (#2858)
ajay-sentry May 8, 2024
4654184
Update useRepoSettings to move to new repository type (#2851)
rohitvinnakota-codecov May 8, 2024
f7ec56d
dep: Update to Sentry RC-1 (#2849)
nicholas-codecov May 8, 2024
88b2e94
fix: Flaky tests around file explorer tables (#2856)
spalmurray-codecov May 8, 2024
1e2da9f
ref: Convert SessionsTable to tanstack table (#2842)
spalmurray-codecov May 8, 2024
87d01b7
style: Use Card component in bundle onboarding (#2861)
spalmurray-codecov May 8, 2024
5622e04
chore: Update codecov.yml so that PR comment isn't sent until 9 uploa…
spalmurray-codecov May 8, 2024
832a683
ref: Convert useComparisonForCommitAndParent to TS and remove reposit…
ajay-sentry May 8, 2024
7f0acc2
feat: Add radio button navigation to repo onboarding (#2839)
spalmurray-codecov May 9, 2024
7102eae
fix: A potential fix for the flaky test (#2854)
RulaKhaled May 9, 2024
0c38e9c
ref: Clean up pull file explorer tests (#2852)
spalmurray-codecov May 9, 2024
1d7f14d
components over time display name instead of id (#2868)
JerrySentry May 9, 2024
3f7c930
style: Fix radio tile group indicator shifting on certain screen size…
spalmurray-codecov May 10, 2024
1f10bec
ref: Remove repositoryDeprecated from usePrefetchSingleFileComp (#2870)
nicholas-codecov May 13, 2024
235d38a
Capitlize view in free alert
RulaKhaled May 13, 2024
35eaa12
Merge branch 'main' into alert-for-paid-plans
RulaKhaled May 13, 2024
2707208
Merge branch 'main' into alert-for-paid-plans
RulaKhaled May 13, 2024
c2ff8d1
oops conflict stuff
RulaKhaled May 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/pages/RepoPage/ActivationAlert/ActivationAlert.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { MemoryRouter, Route } from 'react-router-dom'
import ActivationAlert from './ActivationAlert'

jest.mock('./FreePlanSeatsTakenAlert', () => () => 'FreePlanSeatsTakenAlert')
jest.mock('./PaidPlanSeatsTakenAlert', () => () => 'PaidPlanSeatsTakenAlert')
jest.mock('./ActivationRequiredAlert', () => () => 'ActivationRequiredAlert')
jest.mock('./UnauthorizedRepoDisplay', () => () => 'UnauthorizedRepoDisplay')

const queryClient = new QueryClient()
Expand Down Expand Up @@ -100,4 +102,24 @@ describe('ActivationAlert', () => {
)
expect(freePlanSeatsTakenAlert).toBeInTheDocument()
})

it('renders PaidPlanSeatsTakenAlert when on paid plan and no seats left', async () => {
setup(false, 'users-pro', false)
render(<ActivationAlert />, { wrapper })

const paidPlanSeatsTakenAlert = await screen.findByText(
/PaidPlanSeatsTakenAlert/
)
expect(paidPlanSeatsTakenAlert).toBeInTheDocument()
})

it('renders ActivationRequiredAlert when on paid plan and some seats left', async () => {
setup(false, 'users-pro', true)
render(<ActivationAlert />, { wrapper })

const activationRequiredAlert = await screen.findByText(
/ActivationRequiredAlert/
)
expect(activationRequiredAlert).toBeInTheDocument()
})
})
18 changes: 18 additions & 0 deletions src/pages/RepoPage/ActivationAlert/ActivationAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import { usePlanData } from 'services/account'
import { isFreePlan } from 'shared/utils/billing'

import ActivationRequiredAlert from './ActivationRequiredAlert'
import FreePlanSeatsTakenAlert from './FreePlanSeatsTakenAlert'

Check failure on line 7 in src/pages/RepoPage/ActivationAlert/ActivationAlert.tsx

View workflow job for this annotation

GitHub Actions / Run Type Checker

Duplicate identifier 'FreePlanSeatsTakenAlert'.
import PaidPlanSeatsTakenAlert from './PaidPlanSeatsTakenAlert'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be a leftover from a merge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yah my bad 🏃‍♀️

import FreePlanSeatsTakenAlert from './FreePlanSeatsTakenAlert'

Check failure on line 9 in src/pages/RepoPage/ActivationAlert/ActivationAlert.tsx

View workflow job for this annotation

GitHub Actions / Run Type Checker

Duplicate identifier 'FreePlanSeatsTakenAlert'.
import UnauthorizedRepoDisplay from './UnauthorizedRepoDisplay'

interface URLParams {
Expand All @@ -21,10 +24,25 @@
const renderFreePlanSeatsTakenAlert =
isFreePlan(planData?.plan?.value) && !planData?.plan?.hasSeatsLeft

const renderPaidPlanSeatsTakenAlert =
!isFreePlan(planData?.plan?.value) && !planData?.plan?.hasSeatsLeft

const renderActivationRequiredAlert =
!isFreePlan(planData?.plan?.value) && planData?.plan?.hasSeatsLeft


if (renderFreePlanSeatsTakenAlert) {
return <FreePlanSeatsTakenAlert />
}

if (renderPaidPlanSeatsTakenAlert) {
return <PaidPlanSeatsTakenAlert />
}

if (renderActivationRequiredAlert) {
return <ActivationRequiredAlert />
}

return <UnauthorizedRepoDisplay />
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router-dom'

import ActivationRequiredAlert from './ActivationRequiredAlert'

const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
<MemoryRouter initialEntries={['/gh/codecov/gazebo/new']}>
<Route path="/:provider/:owner/:repo/new">{children}</Route>
</MemoryRouter>
)

describe('ActivationRequiredAlert', () => {
it('renders the banner with correct heading', () => {
render(<ActivationRequiredAlert />, { wrapper })

const bannerHeading = screen.getByRole('heading', {
name: /Activation Required/,
})
expect(bannerHeading).toBeInTheDocument()
})

it('renders the banner with correct description', () => {
render(<ActivationRequiredAlert />, { wrapper })

const description = screen.getByText(
/You have available seats, but activation is needed./
)
expect(description).toBeInTheDocument()
})

it('renders the banner with correct link', () => {
render(<ActivationRequiredAlert />, { wrapper })

const link = screen.getByRole('link', {
name: /Manage members/,
})
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', '/members/gh/codecov')
})

it('renders the correct img', () => {
render(<ActivationRequiredAlert />, { wrapper })

const img = screen.getByAltText('Forbidden')
expect(img).toBeInTheDocument()
expect(img).toHaveAttribute('src', 'error-403.svg')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import img403 from 'layouts/shared/NetworkErrorBoundary/assets/error-403.svg'
import Button from 'ui/Button'

const ActivationRequiredAlert = () => {
return (
<div className="flex flex-col items-center justify-center gap-8 bg-ds-gray-primary pb-28 pt-12 text-center">
<img src={img403} alt="Forbidden" className="w-36" />
<div className="flex w-2/5 flex-col gap-1">
<h1 className="text-2xl">Activation Required</h1>
<p>You have available seats, but activation is needed.</p>
</div>
<Button
to={{ pageName: 'membersTab' }}
disabled={undefined}
hook={undefined}
variant="primary"
>
Manage members
</Button>
</div>
)
}

export default ActivationRequiredAlert
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ActivationRequiredAlert'
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('FreePlanSeatsTakenAlert', () => {
render(<FreePlanSeatsTakenAlert />, { wrapper })

const link = screen.getByRole('link', {
name: /view plan options/,
name: /View plan options/,
})
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', '/plan/gh/codecov')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const FreePlanSeatsTakenAlert = () => {
hook={undefined}
variant="primary"
>
view plan options
View plan options
</Button>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router-dom'

import PaidPlanSeatsTakenAlert from './PaidPlanSeatsTakenAlert'

const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
<MemoryRouter initialEntries={['/gh/codecov/gazebo/new']}>
<Route path="/:provider/:owner/:repo/new">{children}</Route>
</MemoryRouter>
)

describe('PaidPlanSeatsTakenAlert', () => {
it('renders the banner with correct heading', () => {
render(<PaidPlanSeatsTakenAlert />, { wrapper })

const bannerHeading = screen.getByRole('heading', {
name: /Seats Limit Reached/,
})
expect(bannerHeading).toBeInTheDocument()
})

it('renders the banner with correct description', () => {
render(<PaidPlanSeatsTakenAlert />, { wrapper })

const description = screen.getByText(
/Your organization has utilized all available seats on this plan/
)
expect(description).toBeInTheDocument()
})

it('renders the banner with correct link', () => {
render(<PaidPlanSeatsTakenAlert />, { wrapper })

const link = screen.getByRole('link', {
name: /Increase seat count/,
})
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('href', '/plan/gh/codecov/upgrade')
})

it('renders the correct img', () => {
render(<PaidPlanSeatsTakenAlert />, { wrapper })

const img = screen.getByAltText('Forbidden')
expect(img).toBeInTheDocument()
expect(img).toHaveAttribute('src', 'error-403.svg')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import img403 from 'layouts/shared/NetworkErrorBoundary/assets/error-403.svg'
import A from 'ui/A'
import Button from 'ui/Button'

const PaidPlanSeatsTakenAlert = () => {
return (
<div className="flex flex-col items-center justify-center gap-8 bg-ds-gray-primary pb-28 pt-12 text-center">
<img src={img403} alt="Forbidden" className="w-36" />
<div className="flex w-2/5 flex-col gap-1">
<h1 className="text-2xl">Seats Limit Reached</h1>
<p>
Your organization has utilized all available seats on this plan. To
add more members, please increase your seat count.{' '}
<A
to={{ pageName: 'membersTab' }}
isExternal={false}
hook="repo-page-to-members-tab"
variant="semibold"
>
manage members
</A>
</p>
</div>
<Button
to={{ pageName: 'upgradeOrgPlan' }}
disabled={undefined}
hook={undefined}
variant="primary"
>
Increase seat count
</Button>
</div>
)
}

export default PaidPlanSeatsTakenAlert
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './PaidPlanSeatsTakenAlert'
Loading