Elements

73 / 122 · Observability

Quota

How much is left, when it comes back, and the way to get more.

18 messages leftresets in 3h 12m
32 of 50 used

Installation

npx shadcn@latest add "@assistant-ui/elements-quota-banner"

Usage

import { QuotaBanner } from "@/components/elements/quota-banner";

<QuotaBanner used={47} limit={50} unit="messages" resetsIn="3h 12m" upgradeLabel="Upgrade" />

Props

used*numberHow much has been consumed.
limit*numberThe allowance. The banner turns amber at 90 percent.
unit*stringWhat is being counted: messages, runs, credits.
resetsIn*stringPre-formatted time until the window rolls over.
upgradeLabel*stringLabel for the way out.
onUpgrade() => voidCalled from the upgrade action.
classNamestringExtra classes merged onto the root.

Source

quota-banner.tsx
"use client";

import { cn } from "@/lib/utils";
import { inkButton, mono, paper } from "./surfaces";

export function QuotaBanner({
  used,
  limit,
  unit,
  resetsIn,
  upgradeLabel,
  onUpgrade,
  className,
}: {
  used: number;
  limit: number;
  unit: string;
  resetsIn: string;
  upgradeLabel: string;
  onUpgrade?: () => void;
  className?: string;
}) {
  const left = Math.max(0, limit - used);
  const ratio = limit === 0 ? 0 : used / limit;
  const tight = ratio >= 0.9;

  return (
    <div
      className={cn(
        paper,
        "flex w-full max-w-sm flex-col gap-2.5 rounded-2xl p-3.5",
        className,
      )}
    >
      <div className="flex items-baseline gap-2">
        <span
          className={cn(
            "text-[13.5px] font-medium",
            tight && "text-amber-700 dark:text-amber-400",
          )}
        >
          {left} {unit} left
        </span>
        <span className={cn(mono, "text-foreground/30 ms-auto tabular-nums")}>
          resets in {resetsIn}
        </span>
      </div>

      <span className="bg-foreground/[0.06] h-1 w-full overflow-hidden rounded-full">
        <span
          className={cn(
            "block h-full rounded-full transition-[width] duration-500 motion-reduce:transition-none",
            tight ? "bg-amber-500" : "bg-foreground/40",
          )}
          style={{ width: `${Math.min(100, ratio * 100)}%` }}
        />
      </span>

      <div className="flex items-center gap-2">
        <span className={cn(mono, "text-foreground/30 tabular-nums")}>
          {used} of {limit} used
        </span>
        <button
          type="button"
          onClick={onUpgrade}
          className={cn(
            inkButton,
            "ms-auto flex h-7 items-center rounded-full px-3 text-xs font-medium",
          )}
        >
          {upgradeLabel}
        </button>
      </div>
    </div>
  );
}