Infrastructure
Usage Meter code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/usage-meter.blade.php
@props([
'label' => null,
'used' => 0,
'limit' => null,
'unit' => null,
'value' => null,
'limitLabel' => null,
'warnAt' => 75,
'dangerAt' => 90,
'variant' => null,
'size' => 'base',
'icon' => null,
'showValues' => true,
'note' => null,
])
@php
/*
| Metered-usage / quota bar for consumption-based billing. Shows "used of
| limit" with a fill that turns amber past `warnAt` and red past `dangerAt`
| (or when over the limit). Pass numeric `used`/`limit` for automatic
| formatting and percent, or supply pre-formatted `value`/`limitLabel`.
| A null `limit` is treated as unlimited.
|
| size: sm, base, lg
|
| Usage:
| <x-ui.usage-meter label="API calls" :used="8400" :limit="10000" unit="requests" note="Resets Jul 1" />
| <x-ui.usage-meter label="Storage" :used="92" :limit="100" unit="GB" icon="hard-drives" />
| <x-ui.usage-meter label="Seats" :used="12" :limit="null" limit-label="Unlimited" />
*/
$isNumeric = is_numeric($used) && is_numeric($limit) && (float) $limit > 0;
$percent = $isNumeric ? (int) round(((float) $used / (float) $limit) * 100) : 0;
$clampedPercent = max(0, min(100, $percent));
$isOver = $isNumeric && (float) $used > (float) $limit;
$autoVariant = ($isOver || $percent >= (int) $dangerAt)
? 'red'
: ($percent >= (int) $warnAt ? 'amber' : 'brand');
$resolvedVariant = $variant ?? $autoVariant;
$barColors = [
'brand' => 'bg-brand-600 dark:bg-brand-500',
'green' => 'bg-green-600 dark:bg-green-500',
'amber' => 'bg-amber-500 dark:bg-amber-400',
'red' => 'bg-red-600 dark:bg-red-500',
'blue' => 'bg-accent-600 dark:bg-accent-500',
'gray' => 'bg-gray-500 dark:bg-gray-400',
];
$barColor = $barColors[$resolvedVariant] ?? $barColors['brand'];
$heights = ['sm' => 'h-1.5', 'base' => 'h-2.5', 'lg' => 'h-3.5'];
$height = $heights[$size] ?? $heights['base'];
$format = fn ($number) => is_numeric($number) ? rtrim(rtrim(number_format((float) $number, 2), '0'), '.') : (string) $number;
$usedDisplay = $value ?? $format($used);
$limitDisplay = $limit === null ? ($limitLabel ?? 'Unlimited') : ($limitLabel ?? $format($limit));
$unitSuffix = $unit ? ' '.$unit : '';
@endphp
<div {{ $attributes->merge(['class' => 'w-full']) }}>
@if ($label || $showValues)
<div class="mb-1.5 flex items-end justify-between gap-3">
<div class="flex min-w-0 items-center gap-1.5">
@if ($icon)
<x-ui.icon :name="$icon" size="sm" class="shrink-0 text-gray-400 dark:text-gray-500" />
@endif
@if ($label)
<span class="truncate text-sm font-medium text-gray-700 dark:text-gray-300">{{ $label }}</span>
@endif
</div>
@if ($showValues)
<span class="shrink-0 text-sm tabular-nums text-gray-500 dark:text-gray-400">
<span class="font-medium text-gray-900 dark:text-white">{{ $usedDisplay }}</span>
<span class="text-gray-400 dark:text-gray-500"> / {{ $limitDisplay }}{{ $unitSuffix }}</span>
</span>
@endif
</div>
@endif
<div
class="w-full overflow-hidden rounded-full bg-gray-200 dark:bg-gray-800 {{ $height }}"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="{{ $clampedPercent }}"
@if ($label) aria-label="{{ $label }}" @endif
>
<div class="{{ $height }} rounded-full transition-all duration-500 {{ $barColor }}" style="width: {{ $isNumeric ? $clampedPercent : 0 }}%"></div>
</div>
@if ($isNumeric || $note || $isOver)
<div class="mt-1.5 flex items-center justify-between gap-3 text-xs">
<span @class([
'font-medium',
'text-red-600 dark:text-red-400' => $isOver,
'text-gray-500 dark:text-gray-400' => ! $isOver,
])>
@if ($isOver)
<x-ui.icon name="warning" weight="fill" size="xs" class="-mt-0.5 inline" /> Over limit
@elseif ($isNumeric)
{{ $clampedPercent }}% used
@endif
</span>
@if ($note)
<span class="truncate text-gray-400 dark:text-gray-500">{{ $note }}</span>
@endif
</div>
@endif
</div>