Content & marketing
Log Activity Section code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/log-activity-section.blade.php
@props([
'eyebrow' => null,
'number' => null,
'title' => null,
'description' => null,
'align' => 'center',
'entries' => [],
'stream' => 'access.log',
'live' => true,
'spacing' => 'base',
])
@php
/*
| Full-width landing page section for a logging SaaS. A heading block sits
| above a wide, monospace activity feed with colour-coded timestamps,
| subjects, value changes and status badges. Theme-aware surfaces work in
| both light and dark modes without gradients or decorative icons.
|
| Each entry accepts:
| timestamp => string (e.g. '02:14')
| subject => string (entity or resource name)
| status => get|post|put|patch|delete (or update|create|rollback|bulk)
| message => optional plain string
| parts => optional array of text/change/add/code segments
|
| Usage:
| <x-ui.log-activity-section
| number="04"
| eyebrow="Web logs"
| title="Every request, searchable in real time"
| description="HTTP access logs with status codes, latency and method badges."
| />
*/
$spacingClass = ['sm' => 'py-12 sm:py-16', 'base' => 'py-16 sm:py-24', 'lg' => 'py-24 sm:py-32'][$spacing] ?? 'py-16 sm:py-24';
$isCenter = $align === 'center';
$headingWrap = $isCenter ? 'mx-auto max-w-2xl text-center' : 'max-w-2xl';
$defaultEntries = [
[
'timestamp' => '14:02:31',
'subject' => '/api/v1/users',
'status' => 'get',
'parts' => [
['type' => 'code', 'value' => '200'],
['type' => 'text', 'value' => '48ms'],
['type' => 'text', 'value' => '1.2kb'],
],
],
[
'timestamp' => '14:02:28',
'subject' => '/checkout',
'status' => 'post',
'parts' => [
['type' => 'code', 'value' => '201'],
['type' => 'text', 'value' => '142ms'],
['type' => 'text', 'value' => 'order created'],
],
],
[
'timestamp' => '14:02:25',
'subject' => '/assets/app-H3k.css',
'status' => 'get',
'parts' => [
['type' => 'code', 'value' => '304'],
['type' => 'text', 'value' => '8ms'],
['type' => 'text', 'value' => 'cached'],
],
],
[
'timestamp' => '14:02:22',
'subject' => '/login',
'status' => 'post',
'parts' => [
['type' => 'code', 'value' => '401'],
['type' => 'text', 'value' => '19ms'],
['type' => 'text', 'value' => 'invalid credentials'],
],
],
[
'timestamp' => '14:02:19',
'subject' => '/health',
'status' => 'get',
'parts' => [
['type' => 'code', 'value' => '500'],
['type' => 'text', 'value' => '891ms'],
['type' => 'text', 'value' => 'upstream timeout'],
],
],
[
'timestamp' => '14:02:16',
'subject' => '/api/sessions/8f2a',
'status' => 'delete',
'parts' => [
['type' => 'code', 'value' => '204'],
['type' => 'text', 'value' => '33ms'],
],
],
[
'timestamp' => '14:02:14',
'subject' => '/api/settings',
'status' => 'patch',
'parts' => [
['type' => 'code', 'value' => '200'],
['type' => 'text', 'value' => '56ms'],
['type' => 'text', 'value' => 'theme updated'],
],
],
];
$rows = filled($entries) ? $entries : $defaultEntries;
@endphp
<section {{ $attributes->merge(['class' => $spacingClass]) }}>
@if ($eyebrow || $title || $description)
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="{{ $headingWrap }}">
@if ($eyebrow)
<div class="flex items-center gap-2.5 text-sm font-semibold {{ $isCenter ? 'justify-center' : '' }}">
@if ($number)
<span class="inline-flex h-6 min-w-6 items-center justify-center rounded-md bg-brand-600 px-1.5 text-xs font-bold text-white tabular-nums">{{ $number }}</span>
@endif
<span class="uppercase tracking-[0.12em] text-brand-600 dark:text-brand-400">{{ $eyebrow }}</span>
</div>
@endif
@if ($title)
<h2 class="mt-4 text-3xl font-semibold tracking-tight text-gray-900 dark:text-white text-balance sm:text-4xl">{{ $title }}</h2>
@endif
@if ($description)
<p class="mt-4 text-lg leading-relaxed text-gray-600 dark:text-gray-400 text-pretty">{{ $description }}</p>
@endif
</div>
</div>
@endif
<div @class([
'relative',
($eyebrow || $title || $description) ? 'mt-12 sm:mt-16' : null,
])>
<div class="mx-auto w-full max-w-[100rem] px-4 sm:px-6 lg:px-8">
<div class="relative overflow-hidden rounded-2xl bg-white shadow-sm dark:bg-gray-950 sm:rounded-3xl">
<div class="pointer-events-none absolute inset-x-0 top-0 hairline-fade-x" aria-hidden="true"></div>
<div class="relative flex flex-col gap-3 px-4 py-4 sm:flex-row sm:items-center sm:justify-between sm:px-6">
<div class="min-w-0">
<p class="font-mono text-xs uppercase tracking-[0.16em] text-gray-500 dark:text-gray-400">Access log</p>
<p class="mt-1 truncate font-mono text-sm text-gray-900 dark:text-gray-100">{{ $stream }}</p>
</div>
@if ($live)
<div class="flex shrink-0 items-center gap-2">
<span class="relative flex h-2 w-2" aria-hidden="true">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-500 opacity-60 motion-reduce:animate-none"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-green-500"></span>
</span>
<span class="font-mono text-xs uppercase tracking-[0.14em] text-green-700 dark:text-green-400">Live</span>
</div>
@endif
<div class="pointer-events-none absolute inset-x-0 bottom-0 hairline-fade-x" aria-hidden="true"></div>
</div>
<div>
@foreach ($rows as $entry)
<x-ui.log-activity-row
:timestamp="$entry['timestamp'] ?? ''"
:subject="$entry['subject'] ?? ''"
:status="$entry['status'] ?? 'update'"
:message="$entry['message'] ?? null"
:parts="$entry['parts'] ?? []"
:divider="! $loop->last"
/>
@endforeach
</div>
<div class="relative flex flex-wrap items-center justify-between gap-3 px-4 py-3 font-mono text-xs text-gray-500 dark:text-gray-400 sm:px-6">
<div class="pointer-events-none absolute inset-x-0 top-0 hairline-fade-x" aria-hidden="true"></div>
<span>{{ count($rows) }} events in view</span>
<span class="text-gray-400 dark:text-gray-500">Updated just now</span>
</div>
<div class="pointer-events-none absolute inset-x-0 bottom-0 hairline-fade-x" aria-hidden="true"></div>
</div>
</div>
</div>
@if ($slot->isNotEmpty())
<div class="mx-auto mt-10 max-w-7xl px-4 sm:px-6 lg:px-8">
{{ $slot }}
</div>
@endif
</section>