{{-- Stakeholder Report — Page 1 (Overview) §5 page 1: KPI band + peak-this-period hero + needs-attention + Theory-of-Change narrative + theory-health strip. Vars in (from deck): $completedCount int $completedDelta int (may be negative) $goalsOnTrack int $goalsTotal int $overdueCount int $hoursLogged float $needsAttn array — $report['needsAttention'] $logicModel null | {narrative, healthBadges, ...} $hasLM bool --}} {{-- ── KPI band (value-first, delta inline, lowercase label) ──────── --}} @php // Drill-down source data. Show top 5 per cell, "+N more" tail if longer. $completedItems = array_slice($report['milestones']['completed'] ?? [], 0, 5); $completedMoreCount = max(0, count($report['milestones']['completed'] ?? []) - 5); $overdueItems = array_slice($report['needsAttention']['overdueMilestones'] ?? [], 0, 5); $overdueMoreCount = max(0, count($report['needsAttention']['overdueMilestones'] ?? []) - 5); // Denominator counts for KPI context — a raw "3 overdue" doesn't say // "out of how many". Total milestones = completed + inFlight + overdue + // upcoming; open = the not-yet-done subset (inFlight + overdue + upcoming). $inFlightCount = (int) ($stats['inFlight'] ?? 0); $upcomingCount = (int) ($stats['upcoming'] ?? 0); $openMsCount = $inFlightCount + $overdueCount + $upcomingCount; $totalMsCount = $completedCount + $openMsCount; // Drill on "Goals on track" lists the ON-TRACK goals (the number the cell // represents). The count ($goalsOnTrack) comes from $stats, which the engine // derives from the FULL report ($report['goals']) across the strategy AND its // programs — so the drill list must read the same set, not $goalsGroup (which // is scoped to the strategy's own goals only and is empty when goals live on // programs, leaving a "9 on track" cell with an empty list). At-risk goals // surface in the Needs Attention block. $allGoalsForDrill = $report['goals']['goals'] ?? ($goalsGroup['goals'] ?? []); $onTrackAll = array_filter($allGoalsForDrill, fn ($g) => ((array) $g)['status'] === 'status_ontrack' || (is_object($g) && ($g->status ?? '') === 'status_ontrack')); $onTrackAll = array_values($onTrackAll); $onTrackItems = array_slice($onTrackAll, 0, 5); $onTrackMoreCount = max(0, count($onTrackAll) - 5); // Hours drill = per-project effort breakdown, sorted desc. Project names // come from $report['summaries'] (keyed by projectId with .name field). $effortByProj = $report['effort']['byProject'] ?? []; arsort($effortByProj); $projNames = []; foreach (($report['summaries'] ?? []) as $s) { $s = (object) $s; $projNames[(int) ($s->id ?? 0)] = (string) ($s->name ?? ''); } $hoursItems = []; foreach ($effortByProj as $pid => $h) { if ($h <= 0) continue; $hoursItems[] = ['name' => $projNames[(int) $pid] ?? ('#'.$pid), 'hours' => (float) $h]; } $hoursMoreCount = max(0, count($hoursItems) - 5); $hoursItems = array_slice($hoursItems, 0, 5); $fmtDate = fn ($v) => is_object($v) ? $v->setToUserTimezone()->format('M j') : ($v ? date('M j', strtotime((string) $v)) : ''); @endphp
{{ mb_strlen($peakBody) > 260 ? mb_substr($peakBody, 0, 257).'…' : $peakBody }}
@endif