php-mcp/src/Website/Mcp/View/Blade/web/api-explorer.blade.php
2026-01-26 20:57:41 +00:00

219 lines
12 KiB
PHP

<div class="min-h-screen bg-gray-100 py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900">API Explorer</h1>
<p class="mt-2 text-gray-600">Interactive documentation with code snippets in 11 languages</p>
</div>
<!-- API Key Input -->
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
<div class="flex items-start gap-3">
<svg class="w-5 h-5 text-yellow-600 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>
</svg>
<div class="flex-1">
<label for="apiKey" class="block text-sm font-medium text-yellow-800 mb-1">API Key</label>
<div class="flex gap-2">
<input
wire:model="apiKey"
type="password"
id="apiKey"
placeholder="hk_live_..."
class="flex-1 rounded-md border-yellow-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 text-sm"
>
<select
wire:model="baseUrl"
class="rounded-md border-yellow-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 text-sm"
>
<option value="https://api.host.uk.com">Production</option>
<option value="https://api.staging.host.uk.com">Staging</option>
<option value="http://localhost">Local</option>
</select>
</div>
<p class="mt-1 text-xs text-yellow-700">Enter your API key to enable live testing. Keys are not stored.</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Endpoint Selector -->
<div class="lg:col-span-1">
<div class="bg-white rounded-lg shadow">
<div class="p-4 border-b">
<h2 class="text-lg font-semibold">Endpoints</h2>
</div>
<div class="divide-y max-h-[600px] overflow-y-auto">
@foreach($endpoints as $index => $endpoint)
<button
wire:click="selectEndpoint({{ $index }})"
class="w-full text-left p-4 hover:bg-gray-50 transition {{ $selectedEndpoint == $index ? 'bg-indigo-50 border-l-4 border-indigo-500' : '' }}"
>
<div class="flex items-center gap-2 mb-1">
<span class="px-2 py-0.5 rounded text-xs font-medium
{{ $endpoint['method'] === 'GET' ? 'bg-green-100 text-green-800' : '' }}
{{ $endpoint['method'] === 'POST' ? 'bg-blue-100 text-blue-800' : '' }}
{{ $endpoint['method'] === 'PUT' ? 'bg-yellow-100 text-yellow-800' : '' }}
{{ $endpoint['method'] === 'DELETE' ? 'bg-red-100 text-red-800' : '' }}
">
{{ $endpoint['method'] }}
</span>
<span class="font-medium text-gray-900">{{ $endpoint['name'] }}</span>
</div>
<code class="text-xs text-gray-500">{{ $endpoint['path'] }}</code>
</button>
@endforeach
</div>
</div>
</div>
<!-- Code Snippets & Testing -->
<div class="lg:col-span-2 space-y-6">
<!-- Request Configuration -->
<div class="bg-white rounded-lg shadow">
<div class="p-4 border-b">
<h2 class="text-lg font-semibold">Request</h2>
</div>
<div class="p-4 space-y-4">
<div class="flex gap-2">
<select
wire:model.live="method"
class="rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
>
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
</select>
<input
wire:model.live="path"
type="text"
class="flex-1 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 font-mono text-sm"
placeholder="/api/v1/..."
>
</div>
@if(in_array($method, ['POST', 'PUT', 'PATCH']))
<div>
<div class="flex justify-between items-center mb-1">
<label class="block text-sm font-medium text-gray-700">Request Body (JSON)</label>
<button wire:click="formatBody" class="text-xs text-indigo-600 hover:text-indigo-800">Format</button>
</div>
<textarea
wire:model="bodyJson"
rows="4"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 font-mono text-sm"
></textarea>
</div>
@endif
<button
wire:click="sendRequest"
wire:loading.attr="disabled"
class="w-full inline-flex justify-center items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
>
<span wire:loading.remove wire:target="sendRequest">Send Request</span>
<span wire:loading wire:target="sendRequest" class="flex items-center">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Sending...
</span>
</button>
</div>
</div>
<!-- Code Snippets -->
<div class="bg-white rounded-lg shadow">
<div class="p-4 border-b">
<div class="flex justify-between items-center">
<h2 class="text-lg font-semibold">Code Snippet</h2>
<button
wire:click="copyToClipboard"
class="text-sm text-indigo-600 hover:text-indigo-800 flex items-center gap-1"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
</svg>
Copy
</button>
</div>
</div>
<!-- Language Tabs -->
<div class="border-b overflow-x-auto">
<div class="flex px-4">
@foreach($languages as $lang)
<button
wire:click="$set('selectedLanguage', '{{ $lang['code'] }}')"
class="px-3 py-2 text-sm font-medium border-b-2 whitespace-nowrap {{ $selectedLanguage === $lang['code'] ? 'border-indigo-500 text-indigo-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}"
>
{{ $lang['name'] }}
</button>
@endforeach
</div>
</div>
<!-- Code Display -->
<div class="bg-gray-900 rounded-b-lg">
<pre class="p-4 text-sm text-gray-100 overflow-x-auto" style="max-height: 300px;"><code>{{ $snippet }}</code></pre>
</div>
</div>
<!-- Response -->
@if($error)
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
<div class="flex">
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800">Error</h3>
<p class="mt-1 text-sm text-red-700">{{ $error }}</p>
</div>
</div>
</div>
@endif
@if($response)
<div class="bg-white rounded-lg shadow">
<div class="p-4 border-b flex justify-between items-center">
<div class="flex items-center gap-3">
<h2 class="text-lg font-semibold">Response</h2>
<span class="px-2 py-0.5 rounded text-xs font-medium
{{ $response['status'] >= 200 && $response['status'] < 300 ? 'bg-green-100 text-green-800' : '' }}
{{ $response['status'] >= 400 ? 'bg-red-100 text-red-800' : '' }}
">
{{ $response['status'] }}
</span>
</div>
<span class="text-sm text-gray-500">{{ $responseTime }}ms</span>
</div>
<div class="bg-gray-900 rounded-b-lg">
<pre class="p-4 text-sm text-gray-100 overflow-x-auto" style="max-height: 400px;"><code>{{ json_encode($response['body'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) }}</code></pre>
</div>
</div>
@endif
</div>
</div>
<!-- Back Link -->
<div class="mt-8">
<a href="{{ url('/') }}" class="text-indigo-600 hover:text-indigo-800">
&larr; Back to Home
</a>
</div>
</div>
@script
<script>
$wire.on('copy-to-clipboard', ({ code }) => {
navigator.clipboard.writeText(code).then(() => {
// Could show a toast notification here
});
});
</script>
@endscript
</div>