feat(ui): link usage-limit refusals to Prowler pricing (#13184)

This commit is contained in:
Alejandro Bailo authored and GitHub committed 2026-10-28 12:32:50 +01:00
1 parent c0891591a3
commit 06a20bcc7c
42 files changed
+755 -87

No files matched your search

@@ -2,36 +2,22 @@ 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 { PRICING_URL } from "@/lib/external-urls";
import { UsageLimitMessage } from "./usage-limit-message";
describe("UsageLimitMessage", () => {
it("renders the shared usage-limit copy", () => {
it("explains the limit and opens the plans in a new tab", () => {
// When
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);
// Then
expect(
screen.getByText(USAGE_LIMIT_MESSAGE, { exact: false }),
).toBeVisible();
const link = screen.getByRole("link", { name: "View plans" });
expect(link).toHaveAttribute("href", PRICING_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");
});
});
+27 -14
View File
@@ -1,25 +1,38 @@
import Link from "next/link";
import { ToastAction } from "@/components/shadcn/toast";
import { USAGE_LIMIT_MESSAGE } from "@/lib/action-errors";
import { BILLING_URL } from "@/lib/external-urls";
import { PRICING_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.
const VIEW_PLANS_LABEL = "View plans";
/** Inline link to Prowler pricing, in a new tab, for copy that explains the refusal itself. */
export const ViewPlansLink = () => (
<a
href={PRICING_URL}
target="_blank"
rel="noopener noreferrer"
className="underline"
>
{VIEW_PLANS_LABEL}
</a>
);
// Over-limit (trial-expired) notice: the shared copy plus Prowler pricing.
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>
{USAGE_LIMIT_MESSAGE} <ViewPlansLink />
</p>
);
/** Toast action for a usage-limit refusal: Prowler pricing, in a new tab. */
export const usageLimitToastAction = () => (
<ToastAction altText={VIEW_PLANS_LABEL} asChild>
<a href={PRICING_URL} target="_blank" rel="noopener noreferrer">
{VIEW_PLANS_LABEL}
</a>
</ToastAction>
);