Media
Media Stats code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/media-stats.blade.php
@props([
'title' => 'Performance',
'description' => null,
'views' => null,
'watchTime' => null,
'completion' => null,
'avgViewDuration' => null,
'trend' => [],
'items' => [],
])
@php
/*
| Media performance stats: KPI strip plus an optional sparkline trend.
|
| Usage:
| <x-ui.media-stats
| views="12.4k"
| watch-time="186h"
| completion="64%"
| :trend="[12, 18, 16, 24, 30, 28, 36]"
| />
*/
$stats = collect([
['key' => 'views', 'label' => 'Views', 'icon' => 'eye', 'value' => $views],
['key' => 'watchTime', 'label' => 'Watch time', 'icon' => 'clock', 'value' => $watchTime],
['key' => 'completion', 'label' => 'Completion', 'icon' => 'chart-line-up', 'value' => $completion],
['key' => 'avgViewDuration', 'label' => 'Avg. view', 'icon' => 'timer', 'value' => $avgViewDuration],
])
->filter(fn (array $stat) => filled($stat['value']))
->values();
$extra = collect($items)
->map(function ($item) {
return [
'key' => (string) ($item['key'] ?? $item['label'] ?? uniqid('stat_', true)),
'label' => $item['label'] ?? 'Metric',
'icon' => $item['icon'] ?? 'chart-bar',
'value' => $item['value'] ?? null,
];
})
->filter(fn (array $item) => filled($item['value']))
->values();
$all = $stats->merge($extra)->values()->all();
$spark = collect($trend)
->map(fn ($point) => max(0, (float) $point))
->values()
->all();
$sparkMax = max(1, ...( $spark ?: [1] ));
@endphp
<section
{{ $attributes->merge(['class' => 'min-w-0 max-w-full rounded-2xl border border-gray-200 bg-white p-3 shadow-sm dark:border-gray-800 dark:bg-gray-900 sm:p-5']) }}
aria-label="{{ $title }}"
>
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div class="min-w-0">
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</h3>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-400">
{{ $description ?? 'Audience engagement for this media item.' }}
</p>
</div>
@if ($spark !== [])
<div class="flex h-12 w-full items-end gap-1 sm:w-40" aria-hidden="true">
@foreach ($spark as $point)
<span
class="min-w-[4px] flex-1 rounded-t bg-brand-500/80 dark:bg-brand-400/80"
style="height: {{ max(12, (int) round(($point / $sparkMax) * 100)) }}%"
></span>
@endforeach
</div>
@endif
</div>
@if ($all !== [])
<div class="mt-4 grid grid-cols-2 gap-2 sm:grid-cols-4 sm:gap-3">
@foreach ($all as $stat)
<div class="min-w-0 rounded-xl border border-gray-200 bg-gray-50 px-3 py-3 dark:border-gray-800 dark:bg-gray-950/50">
<div class="flex items-center gap-1.5 text-gray-400 dark:text-gray-500">
<x-ui.icon :name="$stat['icon']" size="sm" />
<p class="truncate text-[11px] font-semibold uppercase tracking-wide">{{ $stat['label'] }}</p>
</div>
<p class="mt-2 truncate text-lg font-semibold tabular-nums text-gray-900 dark:text-white">{{ $stat['value'] }}</p>
</div>
@endforeach
</div>
@else
<p class="mt-4 text-sm text-gray-500 dark:text-gray-400">No stats available yet.</p>
@endif
</section>