{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-usage-billing",
  "title": "Billing Usage Billing",
  "description": "Display usage statistics with charts, breakdowns, and trend analysis.",
  "registryDependencies": [
    "button",
    "card",
    "progress",
    "separator"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/billing/billing-usage-billing.tsx",
      "content": "\"use client\";\n\nimport {\n  AlertTriangle,\n  Download,\n  TrendingDown,\n  TrendingUp,\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/new-york/ui/card\";\nimport { Progress } from \"@/registry/new-york/ui/progress\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\n\nexport interface UsageDataPoint {\n  date: Date;\n  value: number;\n  category?: string;\n}\n\nexport interface UsageCategory {\n  name: string;\n  value: number;\n  limit?: number;\n  color?: string;\n}\n\nexport interface BillingUsageBillingProps {\n  currentPeriod: {\n    start: Date;\n    end: Date;\n    usage: number;\n    limit?: number;\n  };\n  previousPeriod?: {\n    usage: number;\n    limit?: number;\n  };\n  dataPoints?: UsageDataPoint[];\n  categories?: UsageCategory[];\n  unit?: string;\n  onDateRangeChange?: (start: Date, end: Date) => void;\n  onExport?: () => void;\n  className?: string;\n  showChart?: boolean;\n  showBreakdown?: boolean;\n  warningThreshold?: number;\n}\n\nfunction formatDate(date: Date): string {\n  const year = date.getFullYear();\n  const month = date.toLocaleString(\"en-US\", { month: \"short\" });\n  const day = date.getDate();\n  return `${month} ${day}, ${year}`;\n}\n\nfunction formatNumber(value: number, unit?: string): string {\n  const formatted = value.toLocaleString(\"en-US\");\n  return unit ? `${formatted} ${unit}` : formatted;\n}\n\nfunction calculateTrend(\n  current: number,\n  previous: number\n): {\n  percentage: number;\n  isIncrease: boolean;\n} {\n  if (previous === 0) {\n    return { percentage: current > 0 ? 100 : 0, isIncrease: current > 0 };\n  }\n  const percentage = ((current - previous) / previous) * 100;\n  return {\n    percentage: Math.abs(percentage),\n    isIncrease: percentage > 0,\n  };\n}\n\nexport default function BillingUsageBilling({\n  currentPeriod,\n  previousPeriod,\n  dataPoints = [],\n  categories = [],\n  unit = \"\",\n  onDateRangeChange,\n  onExport,\n  className,\n  showChart = true,\n  showBreakdown = true,\n  warningThreshold = 80,\n}: BillingUsageBillingProps) {\n  const usagePercentage =\n    currentPeriod.limit !== undefined\n      ? (currentPeriod.usage / currentPeriod.limit) * 100\n      : 0;\n  const isWarning = usagePercentage >= warningThreshold;\n  const isOverLimit =\n    currentPeriod.limit !== undefined &&\n    currentPeriod.usage > currentPeriod.limit;\n\n  const trend = previousPeriod\n    ? calculateTrend(currentPeriod.usage, previousPeriod.usage)\n    : null;\n\n  return (\n    <Card className={cn(\"w-full shadow-xs\", className)}>\n      <CardHeader>\n        <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n          <div className=\"flex flex-col gap-1\">\n            <CardTitle>Usage</CardTitle>\n            <CardDescription>\n              {formatDate(currentPeriod.start)} -{\" \"}\n              {formatDate(currentPeriod.end)}\n            </CardDescription>\n          </div>\n          {onExport && (\n            <Button\n              className=\"w-full sm:w-auto\"\n              onClick={onExport}\n              type=\"button\"\n              variant=\"outline\"\n            >\n              <Download className=\"size-4\" />\n              Export\n            </Button>\n          )}\n        </div>\n      </CardHeader>\n      <CardContent>\n        <div className=\"flex flex-col gap-6\">\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"flex items-center justify-between\">\n              <span className=\"text-sm\">Current period</span>\n              <span className=\"font-medium text-sm\">\n                {formatNumber(currentPeriod.usage, unit)}\n                {currentPeriod.limit !== undefined && (\n                  <> / {formatNumber(currentPeriod.limit, unit)}</>\n                )}\n                {currentPeriod.limit === undefined && \" (Unlimited)\"}\n              </span>\n            </div>\n            {currentPeriod.limit !== undefined && (\n              <Progress\n                aria-label={`Usage: ${usagePercentage.toFixed(0)}%`}\n                className=\"h-2\"\n                value={Math.min(usagePercentage, 100)}\n              />\n            )}\n            {isWarning && !isOverLimit && (\n              <div className=\"flex items-center gap-2 text-xs text-yellow-600\">\n                <AlertTriangle className=\"size-3.5\" />\n                <span>\n                  You&apos;ve used {usagePercentage.toFixed(0)}% of your limit\n                </span>\n              </div>\n            )}\n            {isOverLimit && (\n              <div className=\"flex items-center gap-2 text-destructive text-xs\">\n                <AlertTriangle className=\"size-3.5\" />\n                <span>You&apos;ve exceeded your limit</span>\n              </div>\n            )}\n          </div>\n\n          {previousPeriod && (\n            <>\n              <Separator />\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"flex items-center justify-between\">\n                  <span className=\"text-muted-foreground text-sm\">\n                    Previous period\n                  </span>\n                  <span className=\"text-muted-foreground text-sm\">\n                    {formatNumber(previousPeriod.usage, unit)}\n                    {previousPeriod.limit !== undefined && (\n                      <> / {formatNumber(previousPeriod.limit, unit)}</>\n                    )}\n                  </span>\n                </div>\n                {trend && (\n                  <div className=\"flex items-center gap-2\">\n                    {trend.isIncrease ? (\n                      <TrendingUp className=\"size-4 text-green-600\" />\n                    ) : (\n                      <TrendingDown className=\"size-4 text-blue-600\" />\n                    )}\n                    <span className=\"text-muted-foreground text-xs\">\n                      {trend.isIncrease ? \"Increased\" : \"Decreased\"} by{\" \"}\n                      {trend.percentage.toFixed(1)}% compared to previous period\n                    </span>\n                  </div>\n                )}\n              </div>\n            </>\n          )}\n\n          {showBreakdown && categories.length > 0 && (\n            <>\n              <Separator />\n              <div className=\"flex flex-col gap-4\">\n                <h3 className=\"font-medium text-sm\">Breakdown by category</h3>\n                <div className=\"flex flex-col gap-3\">\n                  {categories.map((category, idx) => {\n                    const categoryPercentage =\n                      category.limit !== undefined\n                        ? (category.value / category.limit) * 100\n                        : 0;\n                    return (\n                      <div className=\"flex flex-col gap-2\" key={idx}>\n                        <div className=\"flex items-center justify-between\">\n                          <span className=\"text-sm\">{category.name}</span>\n                          <span className=\"text-muted-foreground text-sm\">\n                            {formatNumber(category.value, unit)}\n                            {category.limit !== undefined && (\n                              <> / {formatNumber(category.limit, unit)}</>\n                            )}\n                          </span>\n                        </div>\n                        {category.limit !== undefined && (\n                          <Progress\n                            aria-label={`${category.name} usage: ${categoryPercentage.toFixed(0)}%`}\n                            className=\"h-1.5\"\n                            value={Math.min(categoryPercentage, 100)}\n                          />\n                        )}\n                      </div>\n                    );\n                  })}\n                </div>\n              </div>\n            </>\n          )}\n\n          {showChart && dataPoints.length > 0 && (\n            <>\n              <Separator />\n              <div className=\"flex flex-col gap-4\">\n                <h3 className=\"font-medium text-sm\">Usage over time</h3>\n                <div className=\"relative h-32 overflow-hidden rounded-lg border bg-muted/50 p-4\">\n                  <div className=\"flex h-full items-end justify-between gap-1\">\n                    {(() => {\n                      const maxDataValue = Math.max(\n                        ...dataPoints.map((p) => p.value),\n                        1\n                      );\n                      const containerHeight = 128;\n                      const padding = 16;\n                      const availableHeight = containerHeight - padding * 2;\n                      const minHeightPx = 4;\n\n                      return dataPoints.map((point, idx) => {\n                        const heightPercentage =\n                          maxDataValue > 0\n                            ? (point.value / maxDataValue) * 100\n                            : 0;\n                        const calculatedHeightPx = Math.min(\n                          Math.max(\n                            (heightPercentage / 100) * availableHeight,\n                            minHeightPx\n                          ),\n                          availableHeight\n                        );\n\n                        return (\n                          <div\n                            className=\"group relative flex-1\"\n                            key={idx}\n                            title={`${formatDate(point.date)}: ${formatNumber(point.value, unit)}`}\n                          >\n                            <div\n                              className=\"w-full rounded-t bg-primary transition-all hover:bg-primary/80\"\n                              style={{\n                                height: `${calculatedHeightPx}px`,\n                                minHeight: `${minHeightPx}px`,\n                              }}\n                            />\n                          </div>\n                        );\n                      });\n                    })()}\n                  </div>\n                </div>\n                <div className=\"flex items-center justify-between text-muted-foreground text-xs\">\n                  <span>{formatDate(dataPoints[0]?.date || new Date())}</span>\n                  <span>\n                    {formatDate(\n                      dataPoints[dataPoints.length - 1]?.date || new Date()\n                    )}\n                  </span>\n                </div>\n              </div>\n            </>\n          )}\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "billing"
  ],
  "type": "registry:ui"
}