@props([ 'title', 'subtitle' => null, 'points' => [], 'unit' => '', 'color' => 'green', 'trend' => null, 'targetLines' => [], ]) @php $sourcePoints = collect($points)->values(); $values = $sourcePoints->map(fn ($point) => data_get($point, 'value'))->values(); $numericValues = $values->filter(fn ($value) => $value !== null)->map(fn ($value) => (float) $value)->values(); $labels = $sourcePoints->map(fn ($point) => (string) data_get($point, 'label', ''))->values(); $minValue = (float) ($numericValues->min() ?? 0); $targetValues = collect($targetLines)->pluck('value')->filter(fn ($value) => $value !== null)->map(fn ($value) => (float) $value)->values(); $actualMaxValue = (float) max($numericValues->max() ?? 0, $targetValues->max() ?? 0); $buildAxis = function (float $min, float $max) use ($unit): array { if ($min === $max) { $max = $min + 1; } $rawStep = ($max - $min) / 4; $magnitude = 10 ** floor(log10(max($rawStep, 1))); $normalized = $rawStep / $magnitude; $step = match (true) { $normalized <= 1 => $magnitude, $normalized <= 2 => 2 * $magnitude, $normalized <= 5 => 5 * $magnitude, default => 10 * $magnitude, }; $axisMin = $unit === 'clock' || $min < 0 ? floor($min / $step) * $step : 0; $axisMax = max($axisMin + $step, ceil($max / $step) * $step); return [$axisMin, $axisMax, max($step, $axisMax - $axisMin)]; }; [$axisMin, $maxValue, $rangeValue] = $buildAxis($minValue, $actualMaxValue); $averageValue = $numericValues->isNotEmpty() ? (float) $numericValues->avg() : 0; $count = max(1, $values->count()); $plotLeft = 2; $plotRight = 98; $plotTop = 8; $plotBottom = 88; $plotWidth = $plotRight - $plotLeft; $plotHeight = $plotBottom - $plotTop; $svgSegments = []; $currentSegment = []; $dotPoints = []; $axisTicks = collect([1, 0.75, 0.5, 0.25, 0])->map(function (float $ratio) use ($axisMin, $rangeValue, $plotTop, $plotHeight) { return [ 'value' => $axisMin + ($rangeValue * $ratio), 'y' => $plotTop + ((1 - $ratio) * $plotHeight), ]; }); $averageY = $plotBottom - ((($averageValue - $axisMin) / $rangeValue) * $plotHeight); foreach ($values as $index => $value) { if ($value === null) { if ($currentSegment !== []) { $svgSegments[] = implode(' ', $currentSegment); $currentSegment = []; } continue; } $floatValue = (float) $value; $x = $count === 1 ? $plotLeft + ($plotWidth / 2) : $plotLeft + (($index / ($count - 1)) * $plotWidth); $y = $plotBottom - ((($floatValue - $axisMin) / $rangeValue) * $plotHeight); $currentSegment[] = number_format($x, 2, '.', '') . ',' . number_format($y, 2, '.', ''); $dotPoints[] = [ 'x' => $x, 'y' => $y, 'value' => $floatValue, 'label' => (string) data_get($sourcePoints->get($index), 'label', ''), 'date' => (string) data_get($sourcePoints->get($index), 'date', ''), ]; } if ($currentSegment !== []) { $svgSegments[] = implode(' ', $currentSegment); } $firstLabel = $labels->first(); $lastLabel = $labels->last(); $lastValue = $numericValues->last() ?? 0; $totalValue = $numericValues->sum(); $formatNumber = fn (mixed $value): string => rtrim(rtrim(number_format((float) $value, 1, ',', ' '), '0'), ','); $formatValue = function (mixed $value) use ($formatNumber, $unit): string { if ($unit === 'clock') { $minutes = ((int) round((float) $value)) % 1440; $hours = intdiv($minutes, 60); $remaining = $minutes % 60; return str_pad((string) $hours, 2, '0', STR_PAD_LEFT) . 'h' . str_pad((string) $remaining, 2, '0', STR_PAD_LEFT); } if (in_array($unit, ['min', 'minutes'], true)) { $minutes = max(0, (int) round((float) $value)); $hours = intdiv($minutes, 60); $remaining = $minutes % 60; return $hours > 0 ? $hours . 'h' . str_pad((string) $remaining, 2, '0', STR_PAD_LEFT) : $remaining . ' min'; } return trim($formatNumber($value) . ' ' . ($unit === 'clock' ? '' : $unit)); }; $xAxisLabels = $labels ->map(fn (string $label, int $index): array => [ 'label' => $label, 'x' => $labels->count() <= 1 ? 50 : $plotLeft + (($index / max(1, $labels->count() - 1)) * $plotWidth), 'index' => $index, ]) ->values(); $targetRows = collect($targetLines) ->filter(fn (array $line): bool => data_get($line, 'value') !== null) ->map(fn (array $line): array => $line + [ 'y' => $plotBottom - ((((float) $line['value'] - $axisMin) / $rangeValue) * $plotHeight), 'tone' => data_get($line, 'tone', 'target'), ]) ->values(); @endphp
merge(['class' => 'dashboard-panel module-chart-card']) }}>

{{ $subtitle }}

{{ $title }}

@if ($trend) @else {{ $formatValue($lastValue) }} @endif
@foreach ($axisTicks as $tick) @endforeach @foreach ($targetRows as $targetLine) @endforeach @foreach ($svgSegments as $segment) @endforeach
@foreach ($dotPoints as $point) @endforeach
{{ $firstLabel }} → {{ $lastLabel }} {{ __('Min') }} {{ $formatValue($numericValues->min() ?? 0) }} {{ __('Moy.') }} {{ $formatValue($averageValue) }} {{ __('Max') }} {{ $formatValue($actualMaxValue) }} {{ __('Total') }} {{ $formatValue($totalValue) }}
@if ($targetRows->isNotEmpty())
@foreach ($targetRows as $targetLine) {{ data_get($targetLine, 'label', 'Objectif') }} {{ $formatValue(data_get($targetLine, 'value')) }} @endforeach
@endif