mirror of
https://github.com/strapi/strapi.git
synced 2025-08-30 11:45:48 +00:00
chore(content-releases): show Banner reached limit max releases (#19276)
* chore(content-releases): add alert reached max limit pending releases * chore(content-releases): use max pending releases number from config * chore(content-releases): change limits string content * chore(content-releases): unit test to check the limit banner * chore(content-releases): fix ts error * chore(content-releases): fix review comments * chore(content-releases): refactor the solution to use useLicenseLimits * chore(content-releases): fix review comments * chore(content-releases): fix type error * chore(content-releases): fix HeaderLayout wrong height because of subtitle empty on loading * chore(content-releases): remove ReleaseLayout component * chore(content-releases): remove useless translation --------- Co-authored-by: Fernando Chavez <fernando.chavez@strapi.io>
This commit is contained in:
parent
2d371a2d60
commit
cae3a5a17d
@ -5,3 +5,5 @@ export type { Store } from './core/store/configure';
|
||||
export type { SanitizedAdminUser } from '../../shared/contracts/shared';
|
||||
|
||||
export { useDocument as unstable_useDocument } from './hooks/useDocument';
|
||||
// TODO: Replace this export with the same hook exported from the @strapi/admin/strapi-admin/ee in another iteration of this solution
|
||||
export { useLicenseLimits } from '../../ee/admin/src/hooks/useLicenseLimits';
|
||||
|
@ -172,6 +172,13 @@ export interface ReviewWorkflowsFeature {
|
||||
options?: { numberOfWorkflows: number | null; stagesPerWorkflow: number | null };
|
||||
}
|
||||
|
||||
export interface ContentReleasesFeature {
|
||||
name: 'cms-content-releases';
|
||||
options?: {
|
||||
maximumReleases: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: this response needs refactoring because we're mixing the admin seat limit info with
|
||||
* regular EE feature info.
|
||||
@ -185,7 +192,7 @@ export declare namespace GetLicenseLimitInformation {
|
||||
data: {
|
||||
currentActiveUserCount: number;
|
||||
enforcementUserCount: number;
|
||||
features: (SSOFeature | AuditLogsFeature | ReviewWorkflowsFeature)[];
|
||||
features: (SSOFeature | AuditLogsFeature | ReviewWorkflowsFeature | ContentReleasesFeature)[];
|
||||
isHostedOnStrapiCloud: boolean;
|
||||
licenseLimitStatus: unknown;
|
||||
permittedSeats: number;
|
||||
|
@ -1,6 +1,9 @@
|
||||
import * as React from 'react';
|
||||
|
||||
// TODO: Replace this import with the same hook exported from the @strapi/admin/strapi-admin/ee in another iteration of this solution
|
||||
import { useLicenseLimits } from '@strapi/admin/strapi-admin';
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
ContentLayout,
|
||||
@ -44,57 +47,6 @@ import {
|
||||
useCreateReleaseMutation,
|
||||
} from '../services/release';
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* ReleasesLayout
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
interface ReleasesLayoutProps {
|
||||
isLoading?: boolean;
|
||||
totalReleases?: number;
|
||||
onClickAddRelease: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ReleasesLayout = ({
|
||||
isLoading,
|
||||
totalReleases,
|
||||
onClickAddRelease,
|
||||
children,
|
||||
}: ReleasesLayoutProps) => {
|
||||
const { formatMessage } = useIntl();
|
||||
return (
|
||||
<Main aria-busy={isLoading}>
|
||||
<HeaderLayout
|
||||
title={formatMessage({
|
||||
id: 'content-releases.pages.Releases.title',
|
||||
defaultMessage: 'Releases',
|
||||
})}
|
||||
subtitle={
|
||||
!isLoading &&
|
||||
formatMessage(
|
||||
{
|
||||
id: 'content-releases.pages.Releases.header-subtitle',
|
||||
defaultMessage:
|
||||
'{number, plural, =0 {No releases} one {# release} other {# releases}}',
|
||||
},
|
||||
{ number: totalReleases }
|
||||
)
|
||||
}
|
||||
primaryAction={
|
||||
<CheckPermissions permissions={PERMISSIONS.create}>
|
||||
<Button startIcon={<Plus />} onClick={onClickAddRelease}>
|
||||
{formatMessage({
|
||||
id: 'content-releases.header.actions.add-release',
|
||||
defaultMessage: 'New release',
|
||||
})}
|
||||
</Button>
|
||||
</CheckPermissions>
|
||||
}
|
||||
/>
|
||||
{children}
|
||||
</Main>
|
||||
);
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* ReleasesGrid
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
@ -177,6 +129,15 @@ interface CustomLocationState {
|
||||
errors?: Record<'code', string>[];
|
||||
}
|
||||
|
||||
const StyledAlert = styled(Alert)`
|
||||
button {
|
||||
display: none;
|
||||
}
|
||||
p + div {
|
||||
margin-left: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const INITIAL_FORM_VALUES = {
|
||||
name: '',
|
||||
} satisfies FormValues;
|
||||
@ -192,7 +153,10 @@ const ReleasesPage = () => {
|
||||
const [{ query }, setQuery] = useQueryParams<GetReleasesQueryParams>();
|
||||
const response = useGetReleasesQuery(query);
|
||||
const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
|
||||
|
||||
const { getFeature } = useLicenseLimits();
|
||||
const { maximumNumberOfPendingReleases = 3 } = getFeature('cms-content-releases') as {
|
||||
maximumNumberOfPendingReleases: number;
|
||||
};
|
||||
const { isLoading, isSuccess, isError } = response;
|
||||
const activeTab = response?.currentData?.meta?.activeTab || 'pending';
|
||||
const activeTabIndex = ['pending', 'done'].indexOf(activeTab);
|
||||
@ -229,15 +193,14 @@ const ReleasesPage = () => {
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<ReleasesLayout onClickAddRelease={toggleAddReleaseModal} isLoading>
|
||||
<ContentLayout>
|
||||
<LoadingIndicatorPage />
|
||||
</ContentLayout>
|
||||
</ReleasesLayout>
|
||||
<Main aria-busy={isLoading}>
|
||||
<LoadingIndicatorPage />
|
||||
</Main>
|
||||
);
|
||||
}
|
||||
|
||||
const totalReleases = (isSuccess && response.currentData?.meta?.pagination?.total) || 0;
|
||||
const hasReachedMaximumPendingReleases = totalReleases >= maximumNumberOfPendingReleases;
|
||||
|
||||
const handleTabChange = (index: number) => {
|
||||
setQuery({
|
||||
@ -283,9 +246,64 @@ const ReleasesPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ReleasesLayout onClickAddRelease={toggleAddReleaseModal} totalReleases={totalReleases}>
|
||||
<Main aria-busy={isLoading}>
|
||||
<HeaderLayout
|
||||
title={formatMessage({
|
||||
id: 'content-releases.pages.Releases.title',
|
||||
defaultMessage: 'Releases',
|
||||
})}
|
||||
subtitle={formatMessage(
|
||||
{
|
||||
id: 'content-releases.pages.Releases.header-subtitle',
|
||||
defaultMessage: '{number, plural, =0 {No releases} one {# release} other {# releases}}',
|
||||
},
|
||||
{ number: totalReleases }
|
||||
)}
|
||||
primaryAction={
|
||||
<CheckPermissions permissions={PERMISSIONS.create}>
|
||||
<Button
|
||||
startIcon={<Plus />}
|
||||
onClick={toggleAddReleaseModal}
|
||||
disabled={hasReachedMaximumPendingReleases}
|
||||
>
|
||||
{formatMessage({
|
||||
id: 'content-releases.header.actions.add-release',
|
||||
defaultMessage: 'New release',
|
||||
})}
|
||||
</Button>
|
||||
</CheckPermissions>
|
||||
}
|
||||
/>
|
||||
<ContentLayout>
|
||||
<>
|
||||
{activeTab === 'pending' && hasReachedMaximumPendingReleases && (
|
||||
<StyledAlert
|
||||
marginBottom={6}
|
||||
action={
|
||||
<Link href="https://strapi.io/pricing-cloud" isExternal>
|
||||
{formatMessage({
|
||||
id: 'content-releases.pages.Releases.max-limit-reached.action',
|
||||
defaultMessage: 'Explore plans',
|
||||
})}
|
||||
</Link>
|
||||
}
|
||||
title={formatMessage(
|
||||
{
|
||||
id: 'content-releases.pages.Releases.max-limit-reached.title',
|
||||
defaultMessage:
|
||||
'You have reached the {number} pending {number, plural, one {release} other {releases}} limit.',
|
||||
},
|
||||
{ number: maximumNumberOfPendingReleases }
|
||||
)}
|
||||
onClose={() => {}}
|
||||
closeLabel=""
|
||||
>
|
||||
{formatMessage({
|
||||
id: 'content-releases.pages.Releases.max-limit-reached.message',
|
||||
defaultMessage: 'Upgrade to manage an unlimited number of releases.',
|
||||
})}
|
||||
</StyledAlert>
|
||||
)}
|
||||
<TabGroup
|
||||
label={formatMessage({
|
||||
id: 'content-releases.pages.Releases.tab-group.label',
|
||||
@ -355,7 +373,7 @@ const ReleasesPage = () => {
|
||||
initialValues={INITIAL_FORM_VALUES}
|
||||
/>
|
||||
)}
|
||||
</ReleasesLayout>
|
||||
</Main>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,21 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
CheckPermissions: ({ children }: { children: JSX.Element }) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
jest.mock('@strapi/admin/strapi-admin', () => ({
|
||||
...jest.requireActual('@strapi/admin/strapi-admin'),
|
||||
useLicenseLimits: jest.fn().mockReturnValue({
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
license: {
|
||||
enforcementUserCount: 10,
|
||||
licenseLimitStatus: '',
|
||||
permittedSeats: 3,
|
||||
isHostedOnStrapiCloud: false,
|
||||
},
|
||||
getFeature: jest.fn().mockReturnValue({ maximumReleases: 3 }),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Releases home page', () => {
|
||||
it('renders the tab content correctly when there are no releases', async () => {
|
||||
server.use(
|
||||
@ -54,5 +69,11 @@ describe('Releases home page', () => {
|
||||
|
||||
const lastEntry = screen.getByRole('heading', { level: 3, name: 'entry 17' });
|
||||
expect(lastEntry).toBeInTheDocument();
|
||||
|
||||
// Check if you reached the maximum number of releases for license
|
||||
const newReleaseButton = screen.queryByRole('button', { name: /new release/i });
|
||||
expect(newReleaseButton).toBeDisabled();
|
||||
const limitReachedMessage = screen.getByText(/you have reached the 3 pending releases limit/i);
|
||||
expect(limitReachedMessage).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -17,6 +17,9 @@
|
||||
"plugin.name": "Releases",
|
||||
"pages.Releases.title": "Releases",
|
||||
"pages.Releases.header-subtitle": "{number, plural, =0 {No releases} one {# release} other {# releases}}",
|
||||
"pages.Releases.max-limit-reached.title": "You have reached the {number} pending {number, plural, one {release} other {releases}} limit.",
|
||||
"pages.Releases.max-limit-reached.message": "Upgrade to manage an unlimited number of releases.",
|
||||
"pages.Releases.max-limit-reached.action": "Explore plans",
|
||||
"header.actions.add-release": "New Release",
|
||||
"header.actions.refresh": "Refresh",
|
||||
"header.actions.publish": "Publish",
|
||||
|
Loading…
x
Reference in New Issue
Block a user