Infrastructure
Api Keys code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/api-keys.blade.php
@props([
'title' => 'API keys',
'description' => null,
'keys' => [],
'createLabel' => 'Create key',
'createHref' => null,
'emptyMessage' => 'No API keys yet. Create one to start making requests.',
])
@php
/*
| API key manager. Lists issued tokens with a masked secret + copy button,
| scope badges, created / last-used metadata and a per-row actions menu
| (revoke). Pairs naturally with <x-ui.copy-button>. Use the `actions` slot
| for a custom header action, or set `createHref` for the default button.
|
| Each key: [
| 'name' => ?, 'token' => ?(full secret to copy), 'masked' => ?(display),
| 'prefix' => ?, 'scopes' => [...], 'created' => ?, 'lastUsed' => ?,
| 'status' => 'active'|'revoked', 'href' => ?(manage link),
| ]
|
| Usage:
| <x-ui.api-keys :keys="[
| ['name' => 'Production', 'token' => 'sk_live_5Hx…', 'masked' => 'sk_live_••••4242',
| 'scopes' => ['read', 'write'], 'created' => 'Mar 1, 2026', 'lastUsed' => '2 hours ago'],
| ]" create-href="/api-keys/create" />
*/
$normalizedKeys = collect($keys)->map(function (array $key): array {
$token = $key['token'] ?? null;
$prefix = $key['prefix'] ?? null;
$masked = $key['masked']
?? ($token
? ($prefix ? $prefix.'_' : '').'••••••••'.mb_substr((string) $token, -4)
: '••••••••••••');
return [
'name' => $key['name'] ?? 'Untitled key',
'token' => $token,
'masked' => $masked,
'scopes' => array_values($key['scopes'] ?? []),
'created' => $key['created'] ?? null,
'lastUsed' => $key['lastUsed'] ?? $key['last_used'] ?? null,
'status' => $key['status'] ?? 'active',
'href' => $key['href'] ?? null,
];
})->values();
@endphp
<div {{ $attributes->merge(['class' => 'overflow-hidden rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900']) }}>
{{-- Header --}}
<div class="flex flex-col gap-4 border-b border-gray-200 p-5 dark:border-gray-800 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0">
<h3 class="text-base font-semibold text-gray-900 dark:text-white">{{ $title }}</h3>
@if ($description)
<p class="mt-0.5 text-sm text-gray-500 dark:text-gray-400">{{ $description }}</p>
@endif
</div>
<div class="shrink-0">
@isset($actions)
{{ $actions }}
@elseif ($createHref)
<x-ui.button :href="$createHref" size="sm" icon="plus">{{ $createLabel }}</x-ui.button>
@endif
</div>
</div>
{{-- Keys --}}
@if ($normalizedKeys->isNotEmpty())
<ul class="divide-y divide-gray-100 dark:divide-gray-800">
@foreach ($normalizedKeys as $key)
@php $isRevoked = $key['status'] === 'revoked'; @endphp
<li @class([
'flex flex-col gap-3 p-5 sm:flex-row sm:items-center sm:justify-between',
'opacity-60' => $isRevoked,
])>
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<p class="truncate text-sm font-semibold text-gray-900 dark:text-white">{{ $key['name'] }}</p>
@if ($isRevoked)
<x-ui.badge variant="red" size="sm" icon="prohibit">Revoked</x-ui.badge>
@else
<x-ui.badge variant="green" size="sm" dot>Active</x-ui.badge>
@endif
@foreach ($key['scopes'] as $scope)
<x-ui.badge variant="gray" size="sm" :pill="false">{{ $scope }}</x-ui.badge>
@endforeach
</div>
<div class="mt-2 flex items-center gap-2">
<code class="truncate rounded-md bg-gray-100 px-2 py-1 font-mono text-xs text-gray-600 dark:bg-gray-800 dark:text-gray-300">{{ $key['masked'] }}</code>
@if ($key['token'] && ! $isRevoked)
<x-ui.copy-button :value="$key['token']" icon-only size="sm" variant="ghost" aria-label="Copy {{ $key['name'] }} token" />
@endif
</div>
<div class="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-gray-400 dark:text-gray-500">
@if ($key['created'])
<span class="inline-flex items-center gap-1">
<x-ui.icon name="calendar-blank" size="xs" /> Created {{ $key['created'] }}
</span>
@endif
<span class="inline-flex items-center gap-1">
<x-ui.icon name="clock-counter-clockwise" size="xs" />
{{ $key['lastUsed'] ? 'Last used '.$key['lastUsed'] : 'Never used' }}
</span>
</div>
</div>
@unless ($isRevoked)
<div class="flex shrink-0 items-center gap-1 sm:self-start">
<x-ui.dropdown align="right" width="48">
<x-slot:trigger>
<x-ui.icon-button icon="dots-three-vertical" size="sm" label="Manage {{ $key['name'] }}" />
</x-slot:trigger>
@if ($key['href'])
<x-ui.dropdown.item :href="$key['href']" icon="pencil-simple">Edit</x-ui.dropdown.item>
@endif
<x-ui.dropdown.item icon="trash" variant="danger">Revoke</x-ui.dropdown.item>
</x-ui.dropdown>
</div>
@endunless
</li>
@endforeach
</ul>
@else
<div class="flex flex-col items-center justify-center gap-3 px-6 py-12 text-center">
<span class="flex h-11 w-11 items-center justify-center rounded-full bg-gray-100 text-gray-400 dark:bg-gray-800 dark:text-gray-500">
<x-ui.icon name="key" weight="duotone" size="lg" />
</span>
<p class="max-w-xs text-sm text-gray-500 dark:text-gray-400">{{ $emptyMessage }}</p>
@if ($createHref)
<x-ui.button :href="$createHref" size="sm" variant="outline" icon="plus">{{ $createLabel }}</x-ui.button>
@endif
</div>
@endif
</div>