@extends('layouts.app') @push('styles') @include('dashboard.styles') @endpush @section('content')
@if (!empty($clinicWarning ?? null))
{{ $clinicWarning }}
@endif @if (!empty(session('message')))
{{ session('message') }}
@endif @php $analysisState = $ncdAnalysisStatus['state'] ?? 'idle'; $analysisScope = $ncdAnalysisStatus['scope'] ?? 'selected scope'; $analysisStartedAt = $ncdAnalysisStatus['started_at'] ?? null; $analysisFinishedAt = $ncdAnalysisStatus['finished_at'] ?? null; $analysisExitCode = $ncdAnalysisStatus['exit_code'] ?? null; $selectedFilters = $ncdAnalytics['filters'] ?? []; $runObserveDate = old('end_date', request()->query('end_date', $selectedFilters['end_date'] ?? ($ncdConfig['date_range']['end_date'] ?? ''))); $trendGranularity = $selectedFilters['trend_granularity'] ?? 'yearly'; $trendYear = (int) ($selectedFilters['trend_year'] ?? date('Y')); $completedClinicKey = (string) ($ncdAnalysisStatus['requested_clinic'] ?? $clinicConnection); $completedClinicLabel = $clinicOptions[$completedClinicKey] ?? $completedClinicKey; $showReadyAnimation = false; if ($analysisState === 'completed' && !empty($analysisFinishedAt)) { $finishedTs = strtotime((string) $analysisFinishedAt); if (!empty($finishedTs)) { $showReadyAnimation = (time() - $finishedTs) <= 1800; } } @endphp @if ($analysisState === 'running')
NCD analysis is running in background for {{ $analysisScope }} @if (!empty($analysisStartedAt)) (started {{ $analysisStartedAt }}) @endif . You can continue using the dashboard and refresh later.
@elseif ($analysisState === 'completed')
Analysis data is ready. @if (!empty($analysisFinishedAt)) Completed at {{ $analysisFinishedAt }}. @endif @if ($showReadyAnimation)
Clinic: {{ $completedClinicLabel }} Trend: {{ ucfirst($trendGranularity) }} Year: {{ $trendYear }}
@endif
@elseif ($analysisState === 'failed')
NCD analysis failed @if (!is_null($analysisExitCode)) (exit code {{ $analysisExitCode }}) @endif @if (!empty($analysisFinishedAt)) at {{ $analysisFinishedAt }} @endif . Please check server logs and run again.
@endif

Clinic Leader Dr.

NCD analytics

NCD program analytics for {{ $clinicLeader }} and stakeholders.

Clinic
{{ $clinicOptions[$clinicConnection] ?? $clinicConnection }}
@if (!empty($ncdAnalytics['lastUpdated']))
Updated {{ $ncdAnalytics['lastUpdated'] }}
@endif
Action
@csrf
Action
@csrf Action
@if (empty($ncdAnalytics['available']))
No NCD analytics outputs found. Run the analysis first.
@else

{{ ucfirst($trendGranularity) }} cohort and diagnosis trend{{ $trendGranularity === 'monthly' ? ' (' . $trendYear . ')' : '' }}

From ncd_pt_registers: one stacked bar per {{ $trendGranularity === 'yearly' ? 'year' : 'month' }} (total cohort). Hover to see segment values.

Cumulative yearly cohort by diagnosis

From the yearly cohort and diagnosis trend in ncd_pt_registers: cumulative cohort counts by diagnosis bucket (HTN only, DM only, Both, No diagnosis).

{{ ucfirst($trendGranularity) }} cohort trend by gender{{ $trendGranularity === 'monthly' ? ' (' . $trendYear . ')' : '' }}

From ncd_pt_registers: Male/Female stacked cohort counts per {{ $trendGranularity === 'yearly' ? 'year' : 'month' }}.

Age distribution

Based on ncd_pt_registers.visit_Age (5-year bins, ages 1-100)

LTFU from latest appointment (+84 days)

@php $ltfuSummary = $ncdAnalytics['ltfuByAppointment']['summary'] ?? []; $ltfuObserveDate = $ltfuSummary['observe_date'] ?? date('Y-m-d'); $ltfuGraceDays = (int) ($ltfuSummary['grace_days'] ?? 84); $ltfuPatients = (int) ($ltfuSummary['patients'] ?? 0); @endphp

Latest visit per patient: if outcome is Died/Tout, classify as Exited (not Active). Otherwise, if Next_Appointment + {{ $ltfuGraceDays }} days is before observe date ({{ $ltfuObserveDate }}), classify as LTFU; else Active. Patients evaluated: {{ number_format($ltfuPatients) }}.

Clinic visit plan status by year

@php $visitPlan = $ncdAnalytics['visitPlanTrend'] ?? []; $visitPlanSummary = $visitPlan['summary'] ?? []; $onTimeDays = (int) ($visitPlanSummary['on_time_grace_days'] ?? 7); $lateUpperDays = (int) ($visitPlanSummary['late_upper_days'] ?? 84); $visitPlanMinYear = (int) ($visitPlanSummary['min_year'] ?? 2018); $visitPlanCurrentYear = (int) date('Y'); $visitPlanMaxYear = min((int) ($visitPlanSummary['max_year'] ?? $visitPlanCurrentYear), $visitPlanCurrentYear); $visitPlanRtcEvents = (int) ($visitPlanSummary['return_to_care_events'] ?? 0); $visitPlanRtcPatients = (int) ($visitPlanSummary['return_to_care_unique_patients'] ?? 0); $visitPlanLtfuPairs = (int) ($visitPlanSummary['ltfu_pairs'] ?? 0); @endphp

Per patient and visit sequence ({{ $visitPlanMinYear }} to {{ $visitPlanMaxYear }}): compare the next visit date to prior visit’s Next_Appointment. Ontime = 0-{{ $onTimeDays }} days, Unplan = early (<0), Late = {{ $onTimeDays + 1 }}-{{ $lateUpperDays - 1 }} days, LTFU = >={{ $lateUpperDays }} days after appointment, Return to care = later visit after an >={{ $lateUpperDays }} day gap.

Assessed pairs: {{ number_format((int) ($visitPlanSummary['assessed_pairs'] ?? 0)) }}, LTFU pairs: {{ number_format($visitPlanLtfuPairs) }}, return-to-care events: {{ number_format($visitPlanRtcEvents) }}, unique RTC patients: {{ number_format($visitPlanRtcPatients) }}, before {{ $visitPlanMinYear }} (excluded): {{ number_format((int) ($visitPlanSummary['excluded_before_min_year'] ?? 0)) }}, missing Next_Appointment: {{ number_format((int) ($visitPlanSummary['missing_next_appointment'] ?? 0)) }}, no following visit: {{ number_format((int) ($visitPlanSummary['no_following_visit'] ?? 0)) }}.

Return to care by year

@php $rtcTrend = $ncdAnalytics['returnToCareTrend'] ?? []; $rtcSummary = $rtcTrend['summary'] ?? []; $rtcGapDays = (int) ($rtcSummary['gap_days'] ?? 84); @endphp

Return-to-care event: for the same patient, when two consecutive visits are separated by {{ $rtcGapDays }} days or more, the later visit is counted as return to care.

Events: {{ number_format((int) ($rtcSummary['events'] ?? 0)) }}, unique patients: {{ number_format((int) ($rtcSummary['unique_patients'] ?? 0)) }}, pairs under {{ $rtcGapDays }} days: {{ number_format((int) ($rtcSummary['excluded_pairs_under_gap'] ?? 0)) }}.

BP stage comparison

@php $controlStatus = $ncdAnalytics['controlStatus'] ?? []; $controlComparison = $controlStatus['comparison_charts'] ?? []; $controlOverall = $controlComparison['overall'] ?? []; $controlActive = $controlComparison['active'] ?? []; $controlLtfu = $controlComparison['ltfu'] ?? []; $formatComparedSummary = static function (array $rows): string { if (empty($rows)) { return 'None'; } $parts = []; foreach ($rows as $row) { $title = trim((string) ($row['title'] ?? '')); if ($title === '') { continue; } $parts[] = htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . ': ' . number_format((int) ($row['value'] ?? 0)) . ' (' . number_format((float) ($row['percent'] ?? 0), 1) . '%)'; } return !empty($parts) ? implode(', ', $parts) : 'None'; }; @endphp

Hypertension cohort uses ncd_pt_registers.1stHypertension = New/Known. Baseline BP stage uses register priority 3rdBP -> 2ndBP -> 1stBP. Last record stage uses the latest valid follow-up bp_raw on or before the observe date, even if that BP is from an earlier year. Active and LTFU subsets use the same latest appointment +84 day status as the LTFU chart. Patients without a usable baseline or last-record BP stage are shown as Unavailable so the chart covers the whole cohort.

Show data quality details

{{ $controlOverall['title'] ?? 'Overall patients stage comparison' }}

Cohort patients: {{ number_format((int) ($controlOverall['cohort_patients'] ?? 0)) }}. Comparable pairs: {{ number_format((int) ($controlOverall['paired_patients'] ?? 0)) }}.

Compared results: {!! $formatComparedSummary($controlOverall['direction_summary'] ?? []) !!}

{{ $controlActive['title'] ?? 'Active patients stage comparison' }}

Active cohort: {{ number_format((int) ($controlActive['cohort_patients'] ?? 0)) }}. Comparable pairs: {{ number_format((int) ($controlActive['paired_patients'] ?? 0)) }}.

Compared results: {!! $formatComparedSummary($controlActive['direction_summary'] ?? []) !!}

{{ $controlLtfu['title'] ?? 'LTFU patients stage comparison' }}

LTFU cohort: {{ number_format((int) ($controlLtfu['cohort_patients'] ?? 0)) }}. Comparable pairs: {{ number_format((int) ($controlLtfu['paired_patients'] ?? 0)) }}.

Compared results: {!! $formatComparedSummary($controlLtfu['direction_summary'] ?? []) !!}

@foreach (($controlStatus['data_quality'] ?? []) as $row) @endforeach
Data quality
{{ $row['metric'] ?? '' }} {{ number_format((int) ($row['value'] ?? 0)) }}
@php $invalidExamples = $controlStatus['invalid_examples'] ?? []; $invalidBaseline = $invalidExamples['baseline'] ?? []; $invalidLatest = $invalidExamples['last_record'] ?? ($invalidExamples['latest'] ?? []); @endphp
@forelse ($invalidBaseline as $item) @empty @endforelse
Top invalid baseline BP values
{{ $item['value'] ?? '' }} {{ number_format((int) ($item['count'] ?? 0)) }}
None
@forelse ($invalidLatest as $item) @empty @endforelse
Top invalid last-record BP values
{{ $item['value'] ?? '' }} {{ number_format((int) ($item['count'] ?? 0)) }}
None

Glucose status comparison

@php $glucoseStatus = $ncdAnalytics['glucoseStatusComparison'] ?? []; $glucoseComparison = $glucoseStatus['comparison_charts'] ?? []; $glucoseOverall = $glucoseComparison['overall'] ?? []; $glucoseActive = $glucoseComparison['active'] ?? []; $glucoseLtfu = $glucoseComparison['ltfu'] ?? []; @endphp

The diabetes cohort uses ncd_pt_registers.2nd_Hypertension = New/Known/Know. Patients are included in this glucose comparison only if their latest 4 follow-up visits on or before the observe date have NCD_Diagnosis = Diabetes or Both. Baseline glucose uses ncd_pt_registers.1st_tot_Diabetes and 2nd_tot_Diabetes. When the register diabetes diagnosis is New, Known, or Know and baseline glucose is missing, the chart falls back to the earliest follow-up FBS with a valid FBS_test_date where NCD_Diagnosis is Diabetes or Both. The latest side uses the latest follow-up on or before the observe date, then scans that patient's prior 1-year follow-up glucose records. Age is from register visit_Age at baseline, then recalculated at the latest follow-up date. FBS, RBS, and 2HPP are evaluated within that 1-year window, and HbA1c < 7.5% is included when available within 1 year. If any available value exceeds the target, the patient is classified as uncontrolled. Patients without a usable baseline-to-last glucose pair are shown as Unavailable for comparison so the chart covers the whole cohort.

Show data quality details

{{ $glucoseOverall['title'] ?? 'Overall glucose status comparison' }}

Cohort patients: {{ number_format((int) ($glucoseOverall['cohort_patients'] ?? 0)) }}. Comparable pairs: {{ number_format((int) ($glucoseOverall['paired_patients'] ?? 0)) }}.

Compared results: {!! $formatComparedSummary($glucoseOverall['comparison_summary'] ?? []) !!}

{{ $glucoseActive['title'] ?? 'Active glucose status comparison' }}

Active cohort: {{ number_format((int) ($glucoseActive['cohort_patients'] ?? 0)) }}. Comparable pairs: {{ number_format((int) ($glucoseActive['paired_patients'] ?? 0)) }}.

Compared results: {!! $formatComparedSummary($glucoseActive['comparison_summary'] ?? []) !!}

{{ $glucoseLtfu['title'] ?? 'LTFU glucose status comparison' }}

LTFU cohort: {{ number_format((int) ($glucoseLtfu['cohort_patients'] ?? 0)) }}. Comparable pairs: {{ number_format((int) ($glucoseLtfu['paired_patients'] ?? 0)) }}.

Compared results: {!! $formatComparedSummary($glucoseLtfu['comparison_summary'] ?? []) !!}

@foreach (($glucoseStatus['data_quality'] ?? []) as $row) @endforeach
Data quality
{{ $row['metric'] ?? '' }} {{ number_format((int) ($row['value'] ?? 0)) }}
@php $glucoseInvalidExamples = $glucoseStatus['invalid_examples'] ?? []; $glucoseInvalidBaseline = $glucoseInvalidExamples['baseline'] ?? []; $glucoseInvalidLast = $glucoseInvalidExamples['last_record'] ?? []; @endphp
@forelse ($glucoseInvalidBaseline as $item) @empty @endforelse
Top invalid baseline glucose values
{{ $item['value'] ?? '' }} {{ number_format((int) ($item['count'] ?? 0)) }}
None
@forelse ($glucoseInvalidLast as $item) @empty @endforelse
Top invalid latest 1-year follow-up glucose values
{{ $item['value'] ?? '' }} {{ number_format((int) ($item['count'] ?? 0)) }}
None
Legacy calculations are paused on this page. New calculation blocks will be added next.
@endif
Back to clinic dashboard
@endsection @push('scripts') @endpush