shadimage
Billingsdk
blockdetailed-usage-tablesource: ok · hit

Detailed Usage Table

A detailed usage table component with resource consumption breakdown

npx shadcn add "https://billingsdk.com/r/detailed-usage-table.json"
free

Where it goes

data-table

Use cases

dashboard

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

tablecardbadgeutils

Files2 · source cached 10/9/2026, 5:37:50 AM

src/registry/billingsdk/detailed-usage-table.tsx
"use client";

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";

export interface UsageResource {
  name: string;
  used: number;
  limit: number;
  percentage?: number;
  unit?: string;
}

export interface DetailedUsageTableProps {
  className?: string;
  title?: string;
  description?: string;
  resources: UsageResource[];
}

export function DetailedUsageTable({
  className,
  title = "Detailed Usage",
  description,
  resources,
}: DetailedUsageTableProps) {
  const formatNumber = (num: number) => {
    return new Intl.NumberFormat().format(num);
  };

  const getPercentageBar = (percentage: number) => {
    let bgColor = "bg-emerald-500";
    if (percentage >= 90) bgColor = "bg-destructive";
    else if (percentage >= 75) bgColor = "bg-orange-500";

    return (
      <div className="flex min-w-[120px] items-center gap-2">
        <div className="bg-secondary h-2 flex-1 rounded-full">
          <div
            className={cn("h-2 rounded-full transition-all", bgColor)}
            style={{ width: `${Math.max(0, Math.min(percentage, 100))}%` }}
          />
        </div>
        <span className="w-10 text-right text-xs font-medium tabular-nums">
          {Math.round(percentage)}%
        </span>
      </div>
    );
  };

  return (
    <Card className={cn("w-full", className)}>
      <CardHeader>
        <CardTitle>{title}</CardTitle>
        {description && <CardDescription>{description}</CardDescription>}
      </CardHeader>
      <CardContent>
        <div className="overflow-x-auto rounded-md border">
          <Table>
            <TableCaption className="sr-only">
              Detailed usage of resources
            </TableCaption>
            <TableHeader>
              <TableRow>
                <TableHead className="w-[180px]">Resource</TableHead>
                <TableHead className="text-right">Used</TableHead>
                <TableHead className="text-right">Limit</TableHead>
                <TableHead className="min-w-[160px] text-right">
                  Usage
                </TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {resources.length === 0 ? (
                <TableRow>
                  <TableCell
                    colSpan={4}
                    className="text-muted-foreground h-24 text-center"
                  >
                    No resources found
                  </TableCell>
                </TableRow>
              ) : (
                resources.map((resource, index) => {
                  const percentage =
                    resource.percentage ??
                    (resource.limit > 0
                      ? (resource.used / resource.limit) * 100
                      : 0);

                  const unit = resource.unit ? ` ${resource.unit}` : "";

                  return (
                    <TableRow key={resource.name || index}>
                      <TableCell className="font-medium">
                        {resource.name}
                      </TableCell>
                      <TableCell className="text-right tabular-nums">
                        {formatNumber(resource.used)}
                        {unit}
                      </TableCell>
                      <TableCell className="text-muted-foreground text-right tabular-nums">
                        {formatNumber(resource.limit)}
                        {unit}
                      </TableCell>
                      <TableCell className="text-right">
                        {getPercentageBar(percentage)}
                      </TableCell>
                    </TableRow>
                  );
                })
              )}
            </TableBody>
          </Table>
        </div>
      </CardContent>
    </Card>
  );
}
src/registry/billingsdk/demo/detailed-usage-table-demo.tsx
"use client";

import { DetailedUsageTable } from "@/registry/billingsdk/detailed-usage-table";

export function DetailedUsageTableDemo() {
  return (
    <DetailedUsageTable
      title="Resource Usage"
      description="Detailed breakdown of your resource consumption"
      resources={[
        {
          name: "API Calls",
          used: 12300,
          limit: 20000,
          // percentage will be automatically calculated as 61.5%
          unit: "calls",
        },
        { name: "Storage", used: 850, limit: 1000, percentage: 85, unit: "GB" },
        {
          name: "Team Members",
          used: 4,
          limit: 5,
          // percentage will be automatically calculated as 80%
          unit: "users",
        },
        {
          name: "Bandwidth",
          used: 1500,
          limit: 2000,
          percentage: 75,
          unit: "GB",
        },
        {
          name: "Emails",
          used: 8500,
          limit: 10000,
          // percentage will be automatically calculated as 85%
          unit: "emails",
        },
      ]}
    />
  );
}

More from Billingsdk

Browse all components in this library →