Skip to main content

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>