Update pricing UX, billing flows, and API rules
This commit is contained in:
117
app/resources/views/dashboard/user/api-keys.blade.php
Normal file
117
app/resources/views/dashboard/user/api-keys.blade.php
Normal file
@@ -0,0 +1,117 @@
|
||||
@extends('dashboard.app')
|
||||
|
||||
@section('title', 'API Keys')
|
||||
@section('page_title', 'API Keys')
|
||||
@section('page_subtitle', 'Create and manage access tokens for integrations.')
|
||||
|
||||
@section('dashboard_content')
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div class="lg:col-span-2 rounded-3xl glass-card p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Your keys</div>
|
||||
<div class="mt-2 text-xl font-semibold text-white">Manage API access</div>
|
||||
</div>
|
||||
@if ($canCreate)
|
||||
<form method="POST" action="{{ route('dashboard.api-keys.create') }}" class="flex items-center gap-2">
|
||||
@csrf
|
||||
<input type="text" name="name" placeholder="Key name (optional)" class="rounded-full bg-white/5 border border-white/10 px-4 py-2 text-sm text-gray-200 focus:outline-none focus:border-brand-ocean">
|
||||
<button type="submit" class="rounded-full bg-brand-ocean text-white font-semibold px-4 py-2 text-sm">Create key</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
@if (!$canCreate)
|
||||
<div class="mt-4 rounded-2xl border border-amber-400/30 bg-amber-400/10 p-4 text-sm text-amber-200">
|
||||
API keys are available on the Personal plan. Upgrade to unlock API access.
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($newKey)
|
||||
<div class="mt-6 rounded-2xl border border-emerald-400/30 bg-emerald-400/10 p-4 text-sm text-emerald-200">
|
||||
New key created. Copy it now; you won’t be able to see it again.
|
||||
<div class="mt-3 flex items-center gap-2">
|
||||
<code class="rounded-lg bg-black/40 px-3 py-2 text-xs text-emerald-200">{{ $newKey }}</code>
|
||||
<button type="button" data-copy-key="{{ $newKey }}" class="rounded-full border border-emerald-400/40 px-3 py-1 text-xs text-emerald-200 hover:bg-emerald-400/10">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-6 overflow-hidden rounded-2xl border border-white/10">
|
||||
<table class="min-w-full text-sm text-gray-300">
|
||||
<thead class="bg-white/5 text-xs uppercase tracking-[0.2em] text-gray-400">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">Prefix</th>
|
||||
<th class="px-4 py-3 text-left">Name</th>
|
||||
<th class="px-4 py-3 text-left">Created</th>
|
||||
<th class="px-4 py-3 text-left">Last used</th>
|
||||
<th class="px-4 py-3 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($keys as $key)
|
||||
<tr class="border-t border-white/5">
|
||||
<td class="px-4 py-3 font-mono text-xs text-gray-400">{{ $key->key_prefix }}</td>
|
||||
<td class="px-4 py-3 text-white">{{ $key->name ?? '—' }}</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-400">{{ $key->created_at?->toDateString() }}</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-400">{{ $key->last_used_at?->diffForHumans() ?? 'Never' }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
@if ($key->revoked_at)
|
||||
<span class="text-xs text-gray-500">Revoked</span>
|
||||
@else
|
||||
<form method="POST" action="{{ route('dashboard.api-keys.revoke', $key->id) }}" class="inline" data-revoke-form>
|
||||
@csrf
|
||||
<button type="submit" class="rounded-full border border-white/10 px-3 py-1 text-xs text-gray-200 hover:bg-white/10">Revoke</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-4 py-8 text-center text-sm text-gray-500">No API keys yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-3xl glass-card p-6 space-y-4">
|
||||
<div>
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Tips</div>
|
||||
<div class="mt-2 text-xl font-semibold text-white">Keep keys safe</div>
|
||||
</div>
|
||||
<div class="rounded-2xl bg-white/5 border border-white/10 p-4 text-sm text-gray-300">
|
||||
Use separate keys for apps and automate revocation if you suspect compromise.
|
||||
</div>
|
||||
<div class="rounded-2xl bg-white/5 border border-white/10 p-4 text-sm text-gray-300">
|
||||
API keys are required to access private keyword search results.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
(() => {
|
||||
document.querySelectorAll('[data-copy-key]').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(btn.dataset.copyKey || '');
|
||||
btn.textContent = 'Copied';
|
||||
setTimeout(() => { btn.textContent = 'Copy'; }, 1200);
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-revoke-form]').forEach((form) => {
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const ok = await window.dewemojiConfirm('Revoke this API key? This cannot be undone.', {
|
||||
title: 'Revoke API key',
|
||||
okText: 'Revoke',
|
||||
});
|
||||
if (!ok) return;
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user