mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
chore: unify trial expired banner
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
} from "@/components/shadcn/select/select";
|
||||
import { Spinner } from "@/components/shadcn/spinner/spinner";
|
||||
import { TreeStatusIcon } from "@/components/shadcn/tree-view/tree-status-icon";
|
||||
import { UsageLimitMessage } from "@/components/shared/usage-limit-message";
|
||||
import { ToastAction, useToast } from "@/components/ui";
|
||||
import { getActionErrorMessage, hasActionError } from "@/lib/action-errors";
|
||||
import {
|
||||
@@ -362,18 +363,7 @@ export function OrgLaunchScan({
|
||||
)}
|
||||
|
||||
{isBlocked ? (
|
||||
<p className="text-text-error-primary text-sm">
|
||||
You have exceeded the usage limit of one provider. You can add
|
||||
more providers and run unlimited scans by adding a subscription.{" "}
|
||||
<Link
|
||||
href="https://cloud.prowler.com/billing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline"
|
||||
>
|
||||
Manage Billing
|
||||
</Link>
|
||||
</p>
|
||||
<UsageLimitMessage />
|
||||
) : isAdvanced ? (
|
||||
<ScanScheduleFields
|
||||
form={form}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
CloudFeatureBadge,
|
||||
CloudFeatureBadgeLink,
|
||||
} from "@/components/shared/cloud-feature-badge";
|
||||
import { UsageLimitMessage } from "@/components/shared/usage-limit-message";
|
||||
import { ToastAction, useToast } from "@/components/ui";
|
||||
import { EntityInfo } from "@/components/ui/entities";
|
||||
import {
|
||||
@@ -350,20 +351,7 @@ export function LaunchStep({
|
||||
</p>
|
||||
)}
|
||||
|
||||
{(isLimitBlocked || isBlocked) && (
|
||||
<p className="text-text-error-primary text-sm">
|
||||
You have exceeded the usage limit of one provider. You can add more
|
||||
providers and run unlimited scans by adding a subscription.{" "}
|
||||
<Link
|
||||
href="https://cloud.prowler.com/billing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline"
|
||||
>
|
||||
Manage Billing
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
{(isLimitBlocked || isBlocked) && <UsageLimitMessage />}
|
||||
|
||||
{isScheduleMode && (
|
||||
<ScanScheduleFields
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
RadioGroupItem,
|
||||
} from "@/components/shadcn/radio-group/radio-group";
|
||||
import { CloudFeatureBadgeLink } from "@/components/shared/cloud-feature-badge";
|
||||
import { UsageLimitMessage } from "@/components/shared/usage-limit-message";
|
||||
import { FormButtons } from "@/components/ui/form";
|
||||
import { toast, ToastAction } from "@/components/ui/toast";
|
||||
import { getActionErrorMessage, hasActionError } from "@/lib/action-errors";
|
||||
@@ -328,21 +329,6 @@ function LaunchScanForm({
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{isBlocked && (
|
||||
<p className="text-text-error-primary text-sm">
|
||||
You have exceeded the usage limit of one provider. You can add more
|
||||
providers and run unlimited scans by adding a subscription.{" "}
|
||||
<Link
|
||||
href="https://cloud.prowler.com/billing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline"
|
||||
>
|
||||
Manage Billing
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!isScheduleMode && (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="launch-scan-alias">Alias (optional)</FieldLabel>
|
||||
@@ -355,6 +341,8 @@ function LaunchScanForm({
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{isBlocked && <UsageLimitMessage />}
|
||||
|
||||
{isScheduleMode && isScheduleLoading && (
|
||||
<div className="flex items-center gap-3 py-2">
|
||||
<Loader2 className="size-5 animate-spin" />
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { USAGE_LIMIT_MESSAGE } from "@/lib/action-errors";
|
||||
import { BILLING_URL } from "@/lib/external-urls";
|
||||
|
||||
import { UsageLimitMessage } from "./usage-limit-message";
|
||||
|
||||
describe("UsageLimitMessage", () => {
|
||||
it("renders the shared usage-limit copy", () => {
|
||||
render(<UsageLimitMessage />);
|
||||
|
||||
expect(screen.getByText(/exceeded the usage limit/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("links to Prowler Cloud billing", () => {
|
||||
render(<UsageLimitMessage />);
|
||||
|
||||
const link = screen.getByRole("link", { name: /manage billing/i });
|
||||
expect(link).toHaveAttribute("href", BILLING_URL);
|
||||
expect(link).toHaveAttribute("target", "_blank");
|
||||
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
||||
});
|
||||
|
||||
it("keeps the copy in sync with the 402 action-error message", () => {
|
||||
render(<UsageLimitMessage />);
|
||||
|
||||
expect(screen.getByText(USAGE_LIMIT_MESSAGE)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("merges a custom className with the base styles", () => {
|
||||
const { container } = render(<UsageLimitMessage className="mt-4" />);
|
||||
|
||||
expect(container.firstChild).toHaveClass("mt-4");
|
||||
expect(container.firstChild).toHaveClass("text-text-error-primary");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { USAGE_LIMIT_MESSAGE } from "@/lib/action-errors";
|
||||
import { BILLING_URL } from "@/lib/external-urls";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface UsageLimitMessageProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// Over-limit (trial-expired) notice shown in scan launch flows. Pairs the shared
|
||||
// usage-limit copy with a link to Prowler Cloud billing.
|
||||
export const UsageLimitMessage = ({ className }: UsageLimitMessageProps) => (
|
||||
<p className={cn("text-text-error-primary text-sm", className)}>
|
||||
{USAGE_LIMIT_MESSAGE}{" "}
|
||||
<Link
|
||||
href={BILLING_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline"
|
||||
>
|
||||
Manage Billing
|
||||
</Link>
|
||||
</p>
|
||||
);
|
||||
@@ -22,7 +22,7 @@ describe("getActionErrorMessage", () => {
|
||||
expect(message).toBe(ACTION_ERROR_MESSAGES[ACTION_ERROR_STATUS.FORBIDDEN]);
|
||||
});
|
||||
|
||||
it("should use the default subscription error for payment-required responses", () => {
|
||||
it("should use the default usage-limit error for payment-required responses", () => {
|
||||
// Given
|
||||
const result = {
|
||||
error: "Payment required.",
|
||||
|
||||
@@ -6,9 +6,14 @@ export const ACTION_ERROR_STATUS = {
|
||||
export type ActionErrorStatus =
|
||||
(typeof ACTION_ERROR_STATUS)[keyof typeof ACTION_ERROR_STATUS];
|
||||
|
||||
// Shown whenever the API returns 402 for an over-limit (trial-expired) tenant.
|
||||
// Rendered with a billing link by he `UsageLimitMessage` component, and as
|
||||
// plain text in toasts/field errors.
|
||||
export const USAGE_LIMIT_MESSAGE =
|
||||
"You have exceeded the usage limit of one provider. You can add more providers and run unlimited scans by adding a subscription.";
|
||||
|
||||
export const ACTION_ERROR_MESSAGES = {
|
||||
[ACTION_ERROR_STATUS.PAYMENT_REQUIRED]:
|
||||
"Your subscription doesn't allow this action. Upgrade your plan or contact an administrator.",
|
||||
[ACTION_ERROR_STATUS.PAYMENT_REQUIRED]: USAGE_LIMIT_MESSAGE,
|
||||
[ACTION_ERROR_STATUS.FORBIDDEN]:
|
||||
"You don't have permission to perform this action. Ask an administrator to update your role.",
|
||||
} as const satisfies Record<ActionErrorStatus, string>;
|
||||
|
||||
@@ -19,6 +19,9 @@ export const DOCS_URLS = {
|
||||
export const PROWLER_CF_TEMPLATE_URL =
|
||||
"https://prowler-cloud-public.s3.eu-west-1.amazonaws.com/permissions/templates/aws/cloudformation/prowler-scan-role.yml";
|
||||
|
||||
// Prowler Cloud billing/subscription management page.
|
||||
export const BILLING_URL = "https://cloud.prowler.com/billing";
|
||||
|
||||
// AWS Console URL for creating a new StackSet.
|
||||
// Hardcoded to us-east-1 — StackSets are typically managed from this region.
|
||||
// Users in AWS GovCloud or China partitions would need different URLs.
|
||||
|
||||
Reference in New Issue
Block a user