@extends('layouts.app') @php $fmtDt = fn($v) => $v ? \Illuminate\Support\Carbon::parse($v)->format('M j, Y H:i') : '—'; // Build a keyed map so we don't rely on variable variables. $snapshots = [ 'deviations' => $deviations ?? [], 'capa' => $capa ?? [], 'change_control' => $change_control ?? [], 'complaints' => $complaints ?? [], 'audits' => $audits ?? [], ]; $cross = $cross ?? ['aging_buckets' => ['0_7' => 0, '8_30' => 0, '31_90' => 0, '90_plus' => 0]]; // Never print arrays — coerce to int for safe interpolation. $num = function ($value): int { if (is_array($value)) return 0; if (is_bool($value)) return (int) $value; if (is_numeric($value)) return (int) $value; return 0; }; @endphp @section('title', 'KPI Dashboard') @section('content')
{{-- Header --}}

KPI Dashboard

Snapshot generated {{ $fmtDt($generated_at ?? now()) }} · cached {{ \App\Services\KpiService::CACHE_TTL }}s
@csrf
{{-- Cross-module summary --}}
Total Open Across QMS
{{ $num($cross['total_open'] ?? 0) }}
Deviations + CAPA + Changes + Complaints + Audits
Overdue Across QMS
{{ $num($cross['total_overdue'] ?? 0) }}
Past due date and still open
Supplier Requal. Due
{{ $num($cross['supplier_requal_due'] ?? 0) }}
Next 60 days
Aging (30+ Days)
{{ $num(($cross['aging_buckets']['31_90'] ?? 0) + ($cross['aging_buckets']['90_plus'] ?? 0)) }}
{{ $num($cross['aging_buckets']['90_plus'] ?? 0) }} older than 90 days
{{-- Aging buckets --}}
Open Item Age Distribution
@php $buckets = [ '0–7 days' => $num($cross['aging_buckets']['0_7'] ?? 0), '8–30 days' => $num($cross['aging_buckets']['8_30'] ?? 0), '31–90 days' => $num($cross['aging_buckets']['31_90'] ?? 0), '90+ days' => $num($cross['aging_buckets']['90_plus'] ?? 0), ]; $max = max($buckets) ?: 1; @endphp @foreach($buckets as $label => $count)
{{ $label }}
{{ $count }}
@endforeach
{{-- Per-module cards --}} @php $modules = [ [ 'key' => 'deviations', 'label' => 'Deviations & NC', 'icon' => 'exclamation-triangle', 'color' => 'warning', 'route' => 'kpi.deviations', 'open_key' => 'open', 'overdue_key' => 'overdue', 'additional_key' => 'critical', 'additional_label' => 'Critical', ], [ 'key' => 'capa', 'label' => 'CAPA', 'icon' => 'tasks', 'color' => 'success', 'route' => 'kpi.capa', 'open_key' => 'open', 'overdue_key' => 'overdue', 'additional_key' => 'awaiting_effectiveness', 'additional_label' => 'Awaiting Eff.', ], [ 'key' => 'change_control', 'label' => 'Change Control', 'icon' => 'exchange-alt', 'color' => 'primary', 'route' => 'kpi.change-control', 'open_key' => 'open', 'overdue_key' => 'overdue', 'additional_key' => 'pending_approval', 'additional_label' => 'Pending Appr.', ], [ 'key' => 'complaints', 'label' => 'Complaints', 'icon' => 'comment-dots', 'color' => 'info', 'route' => 'kpi.complaints', 'open_key' => 'open', 'overdue_key' => 'reportable_overdue', 'additional_key' => 'critical', 'additional_label' => 'Critical', ], [ 'key' => 'audits', 'label' => 'Audits', 'icon' => 'clipboard-list', 'color' => 'secondary', 'route' => 'kpi.audits', 'open_key' => 'open', 'overdue_key' => 'overdue', 'additional_key' => 'open_findings', 'additional_label' => 'Open Findings', ], ]; @endphp
@foreach($modules as $m) @php $snap = $snapshots[$m['key']] ?? []; @endphp
{{ $m['label'] }}
Total: {{ $num($snap['total'] ?? 0) }}
Details
{{ $num($snap[$m['open_key']] ?? 0) }}
Open
{{ $num($snap[$m['overdue_key']] ?? 0) }}
Overdue
{{ $num($snap[$m['additional_key']] ?? 0) }}
{{ $m['additional_label'] }}
@php $closed = $num($snap['closed_this_month'] ?? 0); @endphp @if($closed > 0)
{{ $closed }} closed this month
@endif
@endforeach
{{-- 30-day trend mini-charts --}}
30-Day Intake Trend
@endsection @push('scripts') @endpush