Chore(webapp): Adds additional billing alerts (#2829)

- Adds 4 additional alert thresholds to ensure customers are emailed if
they have runaway usage.
- Separated these into a new section called "Spike alerts" with a
tooltip so it's clear what they are.
- Tooltip message is: "Catch runaway usage from bugs or errors. We
recommend keeping these enabled as a safety net."
- A billing service PR now returns all orgs to populate the email list,
rather than oldest 5.
- Adds `defaultChecked` logic to honour existing orgs who have
configured alerts in the DB. New orgs get all alerts checked on by
default.

<img width="1316" height="1560" alt="CleanShot 2026-01-05 at 09 43
56@2x"
src="https://github.com/user-attachments/assets/ce749407-2b7f-4864-9c09-9333c5ac495a"
/>
This commit is contained in:
James Ritchie
2026-01-07 14:10:10 +00:00
committed by GitHub
parent c7a238c664
commit 17338050b1
2 changed files with 41 additions and 2 deletions
@@ -6,7 +6,7 @@ import { cn } from "~/utils/cn";
const variantClasses = {
basic:
"bg-background-bright border border-grid-bright rounded px-3 py-2 text-sm text-text-bright shadow-md fade-in-50",
dark: "bg-background-dimmed border border-grid-bright rounded px-3 py-2 text-sm text-text-bright shadow-md fade-in-50"
dark: "bg-background-dimmed border border-grid-bright rounded px-3 py-2 text-sm text-text-bright shadow-md fade-in-50",
};
type Variant = keyof typeof variantClasses;
@@ -111,11 +111,13 @@ export function InfoIconTooltip({
buttonClassName,
contentClassName,
variant = "basic",
disableHoverableContent = false,
}: {
content: React.ReactNode;
buttonClassName?: string;
contentClassName?: string;
variant?: Variant;
disableHoverableContent?: boolean;
}) {
return (
<SimpleTooltip
@@ -125,6 +127,7 @@ export function InfoIconTooltip({
content={content}
variant={variant}
className={contentClassName}
disableHoverableContent={disableHoverableContent}
/>
);
}
@@ -24,12 +24,13 @@ import { Label } from "~/components/primitives/Label";
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
import { Paragraph } from "~/components/primitives/Paragraph";
import { TextLink } from "~/components/primitives/TextLink";
import { InfoIconTooltip } from "~/components/primitives/Tooltip";
import { prisma } from "~/db.server";
import { featuresForRequest } from "~/features.server";
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
import { getBillingAlerts, setBillingAlert } from "~/services/platform.v3.server";
import { requireUserId } from "~/services/session.server";
import { formatCurrency } from "~/utils/numberFormatter";
import { formatCurrency, formatNumber } from "~/utils/numberFormatter";
import {
docsPath,
OrganizationParamsSchema,
@@ -183,6 +184,8 @@ export default function Page() {
const checkboxLevels = [0.75, 0.9, 1.0, 2.0, 5.0];
const spikeAlertLevels = [10.0, 20.0, 50.0, 100.0];
useEffect(() => {
if (alerts.emails.length > 0) {
requestIntent(form.ref.current ?? undefined, list.append(emails.name));
@@ -272,6 +275,39 @@ export default function Page() {
))}
<FormError id={alertLevels.errorId}>{alertLevels.error}</FormError>
</InputGroup>
<InputGroup fullWidth>
<div className="flex items-center gap-1">
<Label>Spike alerts</Label>
<InfoIconTooltip
content={
"Catch runaway usage from bugs or errors. We recommend keeping these enabled as a safety net."
}
disableHoverableContent
/>
</div>
{spikeAlertLevels.map((level) => (
<CheckboxWithLabel
name={alertLevels.name}
id={`level_${level}`}
key={level}
value={level.toString()}
variant="simple/small"
label={
<span>
{formatNumber(level * 100)}%{" "}
<span className="text-text-dimmed">
({formatCurrency(Number(dollarAmount) * level, false)})
</span>
</span>
}
defaultChecked={
alerts.alertLevels.includes(level) ||
!spikeAlertLevels.some((l) => alerts.alertLevels.includes(l))
}
className="pr-0"
/>
))}
</InputGroup>
<InputGroup fullWidth>
<Label htmlFor={emails.id}>Email addresses</Label>
{emailFields.map((email, index) => (