Upgrade prompts

This commit is contained in:
Matt Aitken
2024-06-28 15:00:17 +01:00
parent 82fd3ec43e
commit fead06b797
4 changed files with 32 additions and 63 deletions
@@ -1,20 +1,20 @@
import { ExclamationCircleIcon } from "@heroicons/react/20/solid";
import { ArrowUpCircleIcon } from "@heroicons/react/24/outline";
import tileBgPath from "~/assets/images/error-banner-tile@2x.png";
import { MatchedOrganization } from "~/hooks/useOrganizations";
import { MatchedOrganization, useOrganization } from "~/hooks/useOrganizations";
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
import { formatNumberCompact } from "~/utils/numberFormatter";
import { v3BillingPath } from "~/utils/pathBuilder";
import { LinkButton } from "../../primitives/Buttons";
import { Icon } from "../../primitives/Icon";
import { Paragraph } from "../../primitives/Paragraph";
type UpgradePromptProps = {
runsEnabled: boolean;
runCountCap: number;
planPath: string;
};
export function UpgradePrompt() {
const organization = useOrganization();
const plan = useCurrentPlan();
if (!plan || !plan.v3Usage.hasExceededFreeTier) {
return null;
}
export function UpgradePrompt({ runsEnabled, runCountCap, planPath }: UpgradePromptProps) {
return (
<div
className="flex h-10 items-center justify-between border border-error bg-repeat py-0 pl-3 pr-2"
@@ -23,17 +23,14 @@ export function UpgradePrompt({ runsEnabled, runCountCap, planPath }: UpgradePro
<div className="flex items-center gap-2">
<Icon icon={ExclamationCircleIcon} className="h-5 w-5 text-error" />
<Paragraph variant="small" className="text-error">
{runsEnabled
? `You have exceeded the monthly ${formatNumberCompact(runCountCap)} runs
limit`
: `No runs are executing because you have exceeded the free limit`}
You have exceeded the monthly $
{(plan.v3Subscription?.plan?.limits.includedUsage ?? 500) / 100} free credits.
</Paragraph>
</div>
<LinkButton
variant={"primary/small"}
LeadingIcon={ArrowUpCircleIcon}
leadingIconClassName="px-0"
to={planPath}
to={v3BillingPath(organization)}
>
Upgrade
</LinkButton>
@@ -43,19 +40,6 @@ export function UpgradePrompt({ runsEnabled, runCountCap, planPath }: UpgradePro
export function useShowUpgradePrompt(organization?: MatchedOrganization) {
const currentPlan = useCurrentPlan();
const shouldShow =
organization !== undefined &&
currentPlan !== undefined &&
currentPlan.usage.exceededRunCount &&
currentPlan.usage.runCountCap !== undefined;
if (!shouldShow) {
return { shouldShow };
}
return {
shouldShow,
runCountCap: currentPlan.usage.runCountCap!,
runsEnabled: organization.runsEnabled,
};
const shouldShow = currentPlan?.v3Usage.hasExceededFreeTier === true;
return { shouldShow };
}
@@ -104,7 +104,6 @@ type SideMenuProps = {
export function SideMenu({ user, project, organization, organizations }: SideMenuProps) {
const borderRef = useRef<HTMLDivElement>(null);
const [showHeaderDivider, setShowHeaderDivider] = useState(false);
const { isManagedCloud } = useFeatures();
const currentPlan = useCurrentPlan();
useEffect(() => {
@@ -308,17 +307,12 @@ export function SideMenu({ user, project, organization, organizations }: SideMen
</Button>
}
/>
{currentPlan &&
!currentPlan.v3Subscription?.isPaying &&
currentPlan.v3Subscription.plan &&
currentPlan.v3Usage && (
<FreePlanUsage
to={v3BillingPath(organization)}
percentage={
currentPlan.v3Usage.cents / currentPlan.v3Subscription.plan.limits.includedUsage
}
/>
)}
{currentPlan?.v3Subscription?.isPaying === false && (
<FreePlanUsage
to={v3BillingPath(organization)}
percentage={currentPlan.v3Usage.usagePercentage}
/>
)}
</div>
</div>
</div>
@@ -1,7 +1,6 @@
import { ArrowUpRightIcon } from "@heroicons/react/20/solid";
import { Link, useNavigation } from "@remix-run/react";
import { useOptionalOrganization } from "~/hooks/useOrganizations";
import { useOptionalProject } from "~/hooks/useProject";
import { cn } from "~/utils/cn";
import { plansPath } from "~/utils/pathBuilder";
import { UpgradePrompt, useShowUpgradePrompt } from "../billing/v2/UpgradePrompt";
@@ -23,7 +22,6 @@ export function NavBar({ children }: WithChildren) {
const showUpgradePrompt = useShowUpgradePrompt(organization);
const navigation = useNavigation();
const isLoading = navigation.state === "loading" || navigation.state === "submitting";
const project = useOptionalProject();
return (
<div>
@@ -31,13 +29,7 @@ export function NavBar({ children }: WithChildren) {
<div className="flex w-full items-center justify-between pl-3 pr-1">{children}</div>
<LoadingBarDivider isLoading={isLoading} />
</div>
{showUpgradePrompt.shouldShow && organization && (
<UpgradePrompt
runsEnabled={showUpgradePrompt.runsEnabled}
runCountCap={showUpgradePrompt.runCountCap}
planPath={plansPath(organization)}
/>
)}
{showUpgradePrompt.shouldShow && organization && <UpgradePrompt />}
</div>
);
}
@@ -27,16 +27,9 @@ export function useCurrentPlan(matches?: UIMatch[]) {
matches,
});
const result = data?.currentPlan
? {
...data.currentPlan,
v3Usage: data.usage,
}
: undefined;
console.log(data?.currentPlan);
console.log("useCurrentPlan", result);
return result;
return data?.currentPlan;
}
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
@@ -66,18 +59,24 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
tomorrow.setDate(tomorrow.getDate() + 1);
const usage = await getUsage(organization.id, { from: firstDayOfMonth, to: tomorrow });
let hasExceededFreeTier = false;
let usagePercentage = 0;
if (plan?.v3Subscription && !plan.v3Subscription.isPaying && plan.v3Subscription.plan && usage) {
hasExceededFreeTier = usage.cents > plan.v3Subscription.plan.limits.includedUsage;
usagePercentage = usage.cents / plan.v3Subscription.plan.limits.includedUsage;
}
return typedjson({
organizations,
organization,
project,
isImpersonating: !!impersonationId,
currentPlan: plan,
usage,
currentPlan: { ...plan, v3Usage: { ...usage, hasExceededFreeTier, usagePercentage } },
});
};
export default function Organization() {
const { organization, project, organizations, isImpersonating, usage } =
const { organization, project, organizations, isImpersonating } =
useTypedLoaderData<typeof loader>();
const user = useUser();