Skip to main content

Data display

Monitoring Chart code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/monitoring-chart.blade.php

@props([
    'label',
    'value',
    'type' => 'stacked-bar',
    'labels' => [],
    'series' => [],
    'height' => 200,
    'grid' => false,
    'startLabel' => null,
    'endLabel' => null,
])

@php
    /*
    | Nightwatch-style monitoring panel — uppercase metric label, headline value,
    | inline legend with coloured dots, and a dense time-series chart.
    |
    | type: stacked-bar | line
    | series: [['label' => '1/2/3XX', 'summary' => '123K', 'color' => 'gray', 'data' => [...]]]
    |
    | Usage:
    |   <x-ui.monitoring-chart
    |       label="Requests"
    |       value="124.2K"
    |       type="stacked-bar"
    |       :series="$requestSeries"
    |       start-label="02 Nov 18:00:00 UTC"
    |       end-label="03 Nov 18:00:00 UTC"
    |   />
    */
    $legendDot = fn (string $color): string => match ($color) {
        'amber' => 'bg-amber-500',
        'red' => 'bg-red-500',
        'green' => 'bg-green-600',
        'brand' => 'bg-brand-500',
        default => 'bg-gray-400 dark:bg-gray-500',
    };

    $legendValue = fn (string $color): string => match ($color) {
        'amber' => 'text-amber-500',
        'red' => 'text-red-500',
        'green' => 'text-green-600',
        'brand' => 'text-brand-500',
        default => 'text-gray-900 dark:text-white',
    };

    $normalizedSeries = collect($series)
        ->values()
        ->map(function (array $item): array {
            $color = $item['color'] ?? 'gray';

            // Only pass explicit hex through; named tokens resolve from CSS
            // vars at chart render time so series follow the active theme.
            $chartColor = is_string($color) && str_starts_with($color, '#')
                ? $color
                : null;

            return [
                'label' => $item['label'] ?? '',
                'summary' => $item['summary'] ?? '',
                'color' => $color,
                'chartColor' => $chartColor,
                'data' => array_values($item['data'] ?? []),
            ];
        })
        ->all();

    $config = [
        'type' => $type,
        'labels' => array_values($labels),
        'series' => $normalizedSeries,
        'grid' => (bool) $grid,
    ];

    $pointCount = max(array_map(fn (array $item) => count($item['data'] ?? []), $normalizedSeries) ?: [0]);
    $seriesLabels = collect($normalizedSeries)->pluck('label')->filter()->implode(', ');
    $accessibleLabel = trim(implode(' ', array_filter([
        $label ? $label.' monitoring chart.' : 'Monitoring chart.',
        $seriesLabels !== '' ? 'Series: '.$seriesLabels.'.' : null,
        $pointCount > 0 ? $pointCount.' data point'.($pointCount === 1 ? '' : 's').'.' : null,
    ])));
@endphp

<div {{ $attributes->class(['harbour-monitoring-chart overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950']) }}>
    <div class="px-5 pt-5">
        <p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-gray-500 dark:text-gray-400">
            {{ $label }}
        </p>

        <p class="mt-2 text-3xl font-semibold tracking-tight text-gray-900 tabular-nums dark:text-white">
            {{ $value }}
        </p>

        @if ($normalizedSeries !== [])
            <div class="mt-4 flex flex-wrap gap-x-5 gap-y-2">
                @foreach ($normalizedSeries as $item)
                    <div class="flex items-center gap-2 text-xs">
                        <span @class(['h-2 w-2 shrink-0 rounded-full', $legendDot($item['color'])]) aria-hidden="true"></span>
                        <span class="font-medium text-gray-500 dark:text-gray-400">{{ $item['label'] }}</span>
                        <span @class(['font-semibold tabular-nums', $legendValue($item['color'])])>{{ $item['summary'] }}</span>
                    </div>
                @endforeach
            </div>
        @endif
    </div>

    <div
        x-data="uiMonitoringChart(@js($config))"
        wire:ignore
        class="harbour-monitoring-chart__canvas relative bg-gray-100/80 px-3 pt-2 dark:bg-black/50"
        style="height: {{ is_numeric($height) ? $height.'px' : $height }}"
    >
        <canvas x-ref="canvas" role="img" aria-label="{{ $accessibleLabel }}"></canvas>
    </div>

    @if ($startLabel || $endLabel)
        <div class="flex items-center justify-between px-5 pb-4 text-[11px] text-gray-500 dark:text-gray-400">
            <span>{{ $startLabel }}</span>
            <span>{{ $endLabel }}</span>
        </div>
    @endif
</div>