phase 3
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
<x-app-layout>
|
||||
@php($currentProject = $project)
|
||||
|
||||
<x-slot name="header">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-center gap-2 mb-1 text-sm">
|
||||
<a href="{{ route('projects.issues.show', [$project, $issue]) }}" class="text-gray-500 hover:text-gray-700">← {{ Str::limit($issue->title, 60) }}</a>
|
||||
</div>
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">Event {{ Str::limit($event->event_id, 20) }}</h2>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 shrink-0 ml-4">
|
||||
@if($previousEvent)
|
||||
<a href="{{ route('projects.issues.events.show', [$project, $issue, $previousEvent]) }}"
|
||||
class="px-3 py-1.5 text-sm font-medium rounded-md bg-gray-100 text-gray-700 hover:bg-gray-200 transition">← Older</a>
|
||||
@endif
|
||||
@if($nextEvent)
|
||||
<a href="{{ route('projects.issues.events.show', [$project, $issue, $nextEvent]) }}"
|
||||
class="px-3 py-1.5 text-sm font-medium rounded-md bg-gray-100 text-gray-700 hover:bg-gray-200 transition">Newer →</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-8">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
{{-- Event Meta --}}
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
||||
<div>
|
||||
<span class="text-gray-500">Level</span>
|
||||
<div class="mt-1 font-medium text-gray-900">{{ ucfirst($event->level) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-500">Environment</span>
|
||||
<div class="mt-1 font-medium text-gray-900">{{ $event->environment ?? '—' }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-500">Release</span>
|
||||
<div class="mt-1 font-medium text-gray-900 font-mono">{{ $event->release ?? '—' }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-500">Timestamp</span>
|
||||
<div class="mt-1 font-medium text-gray-900" title="{{ $event->timestamp }}">{{ $event->timestamp->format('Y-m-d H:i:s') }}</div>
|
||||
</div>
|
||||
@if($event->server_name)
|
||||
<div>
|
||||
<span class="text-gray-500">Server</span>
|
||||
<div class="mt-1 font-medium text-gray-900 font-mono">{{ $event->server_name }}</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($event->platform)
|
||||
<div>
|
||||
<span class="text-gray-500">Platform</span>
|
||||
<div class="mt-1 font-medium text-gray-900">{{ $event->platform }}</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($event->sdk)
|
||||
<div>
|
||||
<span class="text-gray-500">SDK</span>
|
||||
<div class="mt-1 font-medium text-gray-900 font-mono text-xs">{{ $event->sdk['name'] ?? '' }} {{ $event->sdk['version'] ?? '' }}</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@if($event->message)
|
||||
<div class="mt-4 p-3 bg-gray-50 rounded-md">
|
||||
<span class="text-xs text-gray-500 font-medium">Message</span>
|
||||
<p class="text-sm text-gray-900 mt-1">{{ $event->message }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Stacktrace --}}
|
||||
@if(!empty($event->exception['values']))
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Exception</h3>
|
||||
</div>
|
||||
@foreach($event->exception['values'] as $exc)
|
||||
<div class="border-b border-gray-100 last:border-b-0">
|
||||
<div class="px-6 py-3 bg-gray-50">
|
||||
<span class="font-mono text-sm font-semibold text-red-700">{{ $exc['type'] ?? 'Exception' }}</span>
|
||||
@if(!empty($exc['value']))
|
||||
<span class="text-sm text-gray-600 ml-2">{{ $exc['value'] }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if(!empty($exc['stacktrace']['frames']))
|
||||
<div x-data="{ expandedFrames: {} }" class="divide-y divide-gray-100">
|
||||
@foreach(array_reverse($exc['stacktrace']['frames']) as $i => $frame)
|
||||
@php
|
||||
$file = $frame['filename'] ?? $frame['abs_path'] ?? '?';
|
||||
$fn = $frame['function'] ?? null;
|
||||
$lineno = $frame['lineno'] ?? null;
|
||||
$inApp = $frame['in_app'] ?? false;
|
||||
$context = $frame['context_line'] ?? null;
|
||||
$preContext = $frame['pre_context'] ?? [];
|
||||
$postContext = $frame['post_context'] ?? [];
|
||||
@endphp
|
||||
<div class="{{ $inApp ? 'bg-white' : 'bg-gray-50/50' }}">
|
||||
<button @click="expandedFrames[{{ $i }}] = !expandedFrames[{{ $i }}]"
|
||||
class="w-full px-6 py-2 text-left text-sm flex items-center gap-2 hover:bg-gray-50 transition">
|
||||
<svg :class="expandedFrames[{{ $i }}] ? 'rotate-90' : ''" class="w-3 h-3 text-gray-400 transition-transform shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M6 4l8 6-8 6V4z"/>
|
||||
</svg>
|
||||
<span class="font-mono {{ $inApp ? 'text-gray-900 font-medium' : 'text-gray-500' }} truncate">
|
||||
{{ $file }}@if($lineno):{{ $lineno }}@endif
|
||||
</span>
|
||||
@if($fn)
|
||||
<span class="text-gray-400 shrink-0">in</span>
|
||||
<span class="font-mono {{ $inApp ? 'text-indigo-700' : 'text-gray-500' }} truncate">{{ $fn }}</span>
|
||||
@endif
|
||||
@if($inApp)
|
||||
<span class="ml-auto shrink-0 inline-flex px-1.5 py-0.5 rounded text-[10px] font-medium bg-indigo-50 text-indigo-600">app</span>
|
||||
@endif
|
||||
</button>
|
||||
<div x-show="expandedFrames[{{ $i }}]" x-cloak class="px-6 pb-3">
|
||||
@if($context || !empty($preContext) || !empty($postContext))
|
||||
<pre class="bg-gray-900 rounded-md p-3 text-xs text-gray-300 overflow-x-auto"><code>@foreach($preContext as $ci => $line)
|
||||
<span class="text-gray-600">{{ ($lineno ?? 0) - count($preContext) + $ci }} {{ $line }}</span>
|
||||
@endforeach
|
||||
@if($context)<span class="text-white bg-red-900/40 block -mx-3 px-3">{{ $lineno }} {{ $context }}</span>@endif
|
||||
@foreach($postContext as $ci => $line)
|
||||
<span class="text-gray-600">{{ ($lineno ?? 0) + $ci + 1 }} {{ $line }}</span>
|
||||
@endforeach</code></pre>
|
||||
@else
|
||||
<p class="text-xs text-gray-400 italic">No source context available.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Breadcrumbs --}}
|
||||
@php
|
||||
$crumbs = $event->breadcrumbs['values'] ?? $event->breadcrumbs ?? [];
|
||||
if (!is_array($crumbs)) $crumbs = [];
|
||||
@endphp
|
||||
@if(count($crumbs) > 0)
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 border-b border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Breadcrumbs</h3>
|
||||
</div>
|
||||
<div class="divide-y divide-gray-100 max-h-80 overflow-y-auto">
|
||||
@foreach($crumbs as $crumb)
|
||||
<div class="px-6 py-2 flex items-start gap-4 text-sm">
|
||||
<span class="shrink-0 text-xs text-gray-400 font-mono w-20 pt-0.5">
|
||||
{{ isset($crumb['timestamp']) ? \Carbon\Carbon::parse($crumb['timestamp'])->format('H:i:s') : '' }}
|
||||
</span>
|
||||
<span class="shrink-0 w-20 text-xs font-medium text-gray-600 truncate">{{ $crumb['category'] ?? $crumb['type'] ?? 'default' }}</span>
|
||||
<span class="text-gray-800 flex-1 truncate">{{ $crumb['message'] ?? json_encode($crumb['data'] ?? '') }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Tags --}}
|
||||
@if(!empty($event->tags))
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">Tags</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@foreach($event->tags as $key => $value)
|
||||
<span class="inline-flex items-center gap-1 px-2.5 py-1 rounded-md bg-gray-100 text-sm">
|
||||
<span class="font-medium text-gray-700">{{ $key }}</span>
|
||||
<span class="text-gray-400">=</span>
|
||||
<span class="text-gray-600">{{ $value }}</span>
|
||||
</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Contexts --}}
|
||||
@php
|
||||
$contexts = $event->contexts ?? [];
|
||||
$userCtx = $event->user_context;
|
||||
$requestCtx = $event->request_context;
|
||||
@endphp
|
||||
@if(!empty($contexts) || $userCtx || $requestCtx)
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
@if($requestCtx)
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
|
||||
<h3 class="text-sm font-medium text-gray-900 mb-3">Request</h3>
|
||||
<dl class="space-y-2 text-sm">
|
||||
@foreach($requestCtx as $k => $v)
|
||||
<div>
|
||||
<dt class="font-medium text-gray-500">{{ ucfirst($k) }}</dt>
|
||||
<dd class="text-gray-900 mt-0.5">
|
||||
@if(is_string($v) || is_numeric($v))
|
||||
<span class="font-mono text-xs">{{ $v }}</span>
|
||||
@else
|
||||
<pre class="bg-gray-50 rounded p-2 text-xs overflow-x-auto mt-1">{{ json_encode($v, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</pre>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
@endforeach
|
||||
</dl>
|
||||
</div>
|
||||
@endif
|
||||
@if($userCtx)
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
|
||||
<h3 class="text-sm font-medium text-gray-900 mb-3">User</h3>
|
||||
<dl class="space-y-2 text-sm">
|
||||
@foreach($userCtx as $k => $v)
|
||||
<div class="flex gap-2">
|
||||
<dt class="font-medium text-gray-500 w-20">{{ ucfirst($k) }}</dt>
|
||||
<dd class="text-gray-900">{{ is_string($v) ? $v : json_encode($v) }}</dd>
|
||||
</div>
|
||||
@endforeach
|
||||
</dl>
|
||||
</div>
|
||||
@endif
|
||||
@foreach($contexts as $ctxName => $ctxData)
|
||||
@if(is_array($ctxData))
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
|
||||
<h3 class="text-sm font-medium text-gray-900 mb-3">{{ ucfirst(str_replace('_', ' ', $ctxName)) }}</h3>
|
||||
<dl class="space-y-2 text-sm">
|
||||
@foreach($ctxData as $k => $v)
|
||||
@if($k !== 'type')
|
||||
<div class="flex gap-2">
|
||||
<dt class="font-medium text-gray-500 w-28 shrink-0">{{ ucfirst(str_replace('_', ' ', $k)) }}</dt>
|
||||
<dd class="text-gray-900 truncate">{{ is_string($v) || is_numeric($v) ? $v : json_encode($v) }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</dl>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Raw JSON --}}
|
||||
<div x-data="{ expanded: false }" class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<button @click="expanded = !expanded" class="w-full p-6 flex items-center justify-between text-left hover:bg-gray-50 transition">
|
||||
<h3 class="text-lg font-medium text-gray-900">Raw JSON</h3>
|
||||
<svg :class="expanded ? 'rotate-180' : ''" class="w-5 h-5 text-gray-400 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div x-show="expanded" x-cloak class="px-6 pb-6">
|
||||
<pre class="bg-gray-900 rounded-md p-4 text-xs text-gray-300 overflow-x-auto max-h-[600px]"><code>{{ json_encode($event->raw_payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
Reference in New Issue
Block a user