Parcourir la source

RPM work matrix (wip)

Vijayakrishnan il y a 3 ans
Parent
commit
398a28c2da

+ 59 - 0
app/Http/Controllers/McpController.php

@@ -430,6 +430,65 @@ class McpController extends Controller
         return view('app.mcp.measurements_pending_stamping_in_care_month', compact('patient', 'careMonth'));
     }
 
+    public function rpm_work_matrix(Request $request) {
+
+        // get the patient having most recent unstamped measurement
+        $performer = $this->performer();
+
+        $ym = ($request->input('y') ?: 'Y') . '-' . ($request->input('m') ?: 'm');
+        $careMonthStart = date($ym . '-01');
+
+        $query = "
+SELECT client.name_first, client.name_last,
+       (client.name_first || ' ' || client.name_last) as client_name,
+       client.uid as client_uid, 
+       client.dob,
+       client.is_enrolled_in_rm,
+       client.most_recent_completed_mcp_note_date,
+       care_month.uid as care_month_uid,
+       care_month.id as care_month_id,
+       care_month.start_date,
+       care_month.rm_total_time_in_seconds_by_mcp,
+       care_month.number_of_days_with_remote_measurements,
+       care_month.has_anyone_interacted_with_client_about_rm_outside_note,
+       care_month.rm_num_measurements_not_stamped_by_mcp,
+       care_month.rm_num_measurements_not_stamped_by_non_hcp,
+       care_month.rm_num_measurements_not_stamped_by_rmm,
+       care_month.rm_num_measurements_not_stamped_by_rme,
+       client.mcp_pro_id,
+       client.default_na_pro_id,
+       client.rmm_pro_id,
+       client.rme_pro_id,
+       client.cell_number,
+       client.most_recent_cellular_bp_dbp_mm_hg,
+       client.most_recent_cellular_bp_sbp_mm_hg,
+       client.most_recent_cellular_bp_measurement_at,      
+       client.most_recent_cellular_weight_value,
+       client.most_recent_cellular_weight_measurement_at,
+       GREATEST(client.most_recent_cellular_bp_measurement_at, client.most_recent_cellular_weight_measurement_at) as latest_measurement_at
+FROM care_month join client on care_month.client_id = client.id
+WHERE
+      client.mcp_pro_id = {$performer->pro->id}
+      AND EXTRACT(MONTH from care_month.start_date) = " . ($request->input('m') ?: 'EXTRACT(MONTH from now())') . "
+      AND EXTRACT(YEAR from care_month.start_date) = " . ($request->input('y') ?: 'EXTRACT(YEAR from now())') . "
+      AND care_month.rm_num_measurements_not_stamped_by_mcp > 0
+      ORDER BY latest_measurement_at DESC
+      LIMIT 1
+";
+
+        $patient = null;
+        $careMonth = null;
+        $patients = DB::select($query);
+
+        if(count($patients)) {
+            $patient = Client::where('uid', $patients[0]->client_uid)->first();
+            $careMonth = CareMonth::where('uid', $patients[0]->care_month_uid)->first();
+        }
+
+        return view('app.mcp.rpm_work_matrix', compact('patient', 'careMonth'));
+
+    }
+
     public function measurements_mass_stamping(Request $request){
         $careMonthsWithMeasurementsPendingStamping = CareMonth::select('id')
             ->where('mcp_pro_id', $this->performer->pro->id)

+ 2 - 1
app/Models/Client.php

@@ -35,8 +35,9 @@ class Client extends Model
         return $this->hasOne(ClientPrimaryCoverage::class, 'id', 'temporary_outsider_new_client_primary_coverage_id');
     }
 
-    public function displayName()
+    public function displayName($_flat = false)
     {
+        if($_flat) return $this->name_first . ' ' . $this->name_last;
         return $this->name_last . ', ' . $this->name_first;
     }
 

+ 1 - 1
resources/views/app/mcp/measurements_pending_stamping_in_care_month.blade.php

@@ -10,7 +10,7 @@
 
         <div class="card-body p-0 border-0">
 
-            @include('app.patient.care-month._matrix', ['onlyMatrix' => true])
+            @include('app.patient.care-month._matrix')
 
         </div>
     </div>

+ 27 - 0
resources/views/app/mcp/rpm_work_matrix.blade.php

@@ -0,0 +1,27 @@
+<div class="px-3 pt-3 mcp-theme-1">
+    <div class="card border-0">
+
+        <div class="card-header px-3 py-2 d-flex align-items-center border-bottom-0 hide-inside-popup">
+            <strong class="mr-4">
+                <i class="fas fa-user"></i>
+                {{$patient ? $patient->displayName() . ': ' : ''}}: Measurements Pending Stamping
+            </strong>
+        </div>
+
+        <div class="card-body p-0 border-0">
+
+            @if(!$patient || !$careMonth)
+                <div class="py-4 text-secondary">
+                    No more patients with measurements pending standing!
+                </div>
+            @else
+                @include('app.patient.care-month._work_matrix')
+            @endif
+
+        </div>
+    </div>
+
+</div>
+
+
+

+ 173 - 0
resources/views/app/patient/care-month/_vitals_graph.blade.php

@@ -0,0 +1,173 @@
+
+    <link href="/c3/c3.min.css" rel="stylesheet">
+    <script src="/c3/d3.v5.min.js" charset="utf-8"></script>
+    <script src="/c3/c3.min.js"></script>
+
+    <div id="vitalsGraphComponent" class="stag-chart mb-4 pt-3">
+        <h4 class="font-weight-bold mb-1 text-secondary font-size-14 text-center">Blood Pressure</h4>
+        <div id="bp-chart" class="mb-4">BP Graph</div>
+        <hr class="my-3">
+        <h4 class="font-weight-bold mb-1 text-secondary font-size-14 text-center">Weight</h4>
+        <div id="weight-chart">Weight Graph</div>
+    </div>
+
+    <?php
+    $dates = [];
+    $startDate = $careMonth->start_date;
+    $nextMonthFirstDay = date_format(date_add(date_create($startDate), date_interval_create_from_date_string("1 month")), 'Y-m-d');
+
+    $nextDay = $startDate;
+    while ($nextDay !== $nextMonthFirstDay) {
+        $dates[] = $nextDay;
+        $nextDay = date_format(date_add(date_create($nextDay), date_interval_create_from_date_string('1 day')), 'Y-m-d');
+    }
+
+    /** @var \App\Models\Client $patient */
+
+    // BP
+    $bpMeasurements = $patient->getNonZeroBpMeasurements->toArray();
+    $weightMeasurements = $patient->getNonZeroWeightMeasurements->toArray();
+
+    $bpData = [];
+    $weightData = [];
+
+    for ($i=0; $i<count($dates); $i++) {
+
+        $date = $dates[$i];
+
+        // bp
+        $bp = array_filter($bpMeasurements, function($_measurement) use ($date) {
+            return $_measurement['effective_date'] === $date;
+        });
+        if(count($bp)) {
+            $bp = array_values($bp);
+            $bp = $bp[count($bp) - 1];
+        }
+        else {
+            $bp = null;
+        }
+
+
+        if ($bp) {
+            $bpData[] = [
+                "date" => $date,
+                "sbp" => $bp["sbp_mm_hg"],
+                "dbp" => $bp["dbp_mm_hg"]
+            ];
+        }
+
+        // weight
+        $weight = array_filter($weightMeasurements, function($_measurement) use ($date) {
+            return $_measurement['effective_date'] === $date;
+        });
+        if(count($weight)) {
+            $weight = array_values($weight);
+            $weight = $weight[count($weight) - 1];
+            $weightData[] = [
+                "date" => $date,
+                "weight" => $weight["numeric_value"]
+            ];
+        }
+
+    }
+
+    $bpDates = [];
+    $sbpValues = [];
+    $dbpValues = [];
+    for ($i = 0; $i < count($bpData); $i++) {
+        $bpDates[] = $bpData[$i]['date'];
+        $sbpValues[] = $bpData[$i]['sbp'];
+        $dbpValues[] = $bpData[$i]['dbp'];
+    }
+
+    $weightDates = [];
+    $weightValues = [];
+    for ($i = 0; $i < count($weightData); $i++) {
+        $weightDates[] = $weightData[$i]['date'];
+        $weightValues[] = $weightData[$i]['weight'];
+    }
+
+    ?>
+
+    <script>
+        (function() {
+            function init() {
+                bpChart();
+                weightChart();
+            }
+            function bpChart() {
+                window.vgBPChart = c3.generate({
+                    bindto: '#bp-chart',
+                    data: {
+                        x: 'x',
+                        // xFormat: '%Y%m%d', // 'xFormat' can be used as custom format of 'x'
+                        columns: [
+                            ['x', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $bpDates)) ?>],
+                            ['Systolic BP', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $sbpValues)) ?>],
+                            ['Diastolic BP', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $dbpValues)) ?>]
+                        ]
+                    },
+                    axis: {
+                        x: {
+                            type: 'timeseries',
+                            tick: {
+                                format: '%Y-%m-%d',
+                                multiline: true,
+                                fit: true,
+                                rotate: -45
+                            },
+                        },
+                        y: {
+                            show: true,
+                            label: {
+                                text: 'Blood Pressure (mmHg)',
+                                position: 'outer-middle'
+                            },
+                            min: 60,
+                            max: 220
+                        },
+                    },
+                    regions: [
+                        {axis: 'y', start: 100, end: 130, class: 'safe-region', label: 'Safe Systolic BP: 100 to 130 mmHg'},
+                        {axis: 'y', start: 60, end: 90, class: 'safe-region', label: 'Safe Diastolic BP: 60 to 90 mmHg'}
+                    ]
+                });
+            }
+            function weightChart() {
+                window.vgWtChart = c3.generate({
+                    bindto: '#weight-chart',
+                    data: {
+                        x: 'x',
+                        columns: [
+                            ['x', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $weightDates)) ?>],
+                            ['Weight', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $weightValues)) ?>]
+                        ]
+                    },
+                    axis: {
+                        x: {
+                            type: 'timeseries',
+                            tick: {
+                                format: '%Y-%m-%d',
+                                multiline: true,
+                                fit: true,
+                                rotate: -45
+                            },
+                        },
+                        y: {
+                            show: true,
+                            label: {
+                                text: 'Weight (lbs)',
+                                position: 'outer-middle'
+                            },
+                            min: 70,
+                            max: 250
+                        },
+                    },
+                    regions: [
+                        // {axis: 'y', start: 100, end: 140, class: 'safe-region', label: 'Safe Weight: 100 to 140 lbs'},
+                    ]
+                });
+            }
+            addMCInitializer('vitalsGraph', init, '#vitalsGraphComponent');
+        }).call(window);
+    </script>

+ 564 - 0
resources/views/app/patient/care-month/_work_matrix.blade.php

@@ -0,0 +1,564 @@
+<?php
+
+$days = $patient->cmMeasurementsMatrix($careMonth, ($pro->pro_type === 'ADMIN' ? null : $pro));
+
+$performerRole = false;
+if ($pro->pro_type === 'ADMIN') {
+    $performerRole = 'ADMIN';
+} else if ($careMonth->mcp_pro_id === $pro->id) {
+    $performerRole = 'MCP';
+} else if ($careMonth->rme_pro_id === $pro->id || $careMonth->rmm_pro_id === $pro->id) {
+    $performerRole = 'NON-HCP';
+}
+
+$unstampedMCP = [];
+$unstampedNonHCP = [];
+foreach ($days as $k => $day) {
+    $unstampedMCP[$k] = [];
+    $unstampedNonHCP[$k] = [];
+    foreach ($days[$k] as $mIndex => $m) {
+        if(!$m->has_been_stamped_by_mcp) {
+            $unstampedMCP[$k][] = $m->measurement_uid;
+        }
+        if(!$m->has_been_stamped_by_non_hcp) {
+            $unstampedNonHCP[$k][] = $m->measurement_uid;
+        }
+    }
+}
+
+// skip days that dont have any unstamped
+$daysWithUnstamped = [];
+foreach ($days as $k => $day) {
+    foreach ($days[$k] as $mIndex => $m) {
+        if($performerRole === 'MCP') {
+            if(!$m->has_been_stamped_by_mcp) {
+                $daysWithUnstamped[$k] = $days[$k];
+                break;
+            }
+        }
+    }
+}
+$days = $daysWithUnstamped;
+
+?>
+
+<div id="rpm-work-matrix">
+
+    <div class="m-neg-3 border-top border-bottom px-3 py-2 d-flex align-items-baseline mb-3 bg-light">
+        <b>Patient:</b>
+        <span class="ml-1">{{$patient->displayName(true)}}</span>
+        <b class="ml-4">Care Month:</b>
+        <span class="ml-1">{{friendly_month($careMonth->start_date)}}</span>
+    </div>
+
+@if(!@$onlyMatrix)
+<div class="d-flex align-items-baseline mb-3" tab-links>
+    <a class="tab-link-active" href="#" tab-link="matrix">Matrix</a>
+    <a class="" href="#" tab-link="measurements-graph">Graph</a>
+    <a class="" href="#" tab-link="bmi-summary">BMI/Weight Management Settings</a>
+    <a class="" href="#" tab-link="bp-summary">BP Management Settings</a>
+</div>
+@endif
+<table class="table table-condensed table-sm table-bordered mb-3 cm-tab" tab-key="matrix">
+    <thead class="bg-light">
+    <tr>
+        <th class="px-2 text-secondary align-bottom border-bottom-0" rowspan="2">Date</th>
+        <th class="px-2 text-secondary border-bottom-0" colspan="4">Measurements</th>
+        <th class="px-2 text-secondary align-bottom border-bottom-0" rowspan="2">{{$performerRole === 'ADMIN' ? 'Stamp Status' : 'Stamp + Entry'}}</th>
+        <th class="px-2 text-secondary border-bottom-0" rowspan="2">Entries</th>
+    </tr>
+    <tr>
+        <th class="text-secondary text-sm border-bottom-0">Time</th>
+        <th class="text-secondary text-sm border-bottom-0">Type</th>
+        <th class="text-secondary text-sm border-bottom-0">Value</th>
+        <th class="text-secondary text-sm border-bottom-0">Stamp</th>
+    </tr>
+    </thead>
+    <tbody>
+    @foreach($days as $k => $day)
+        @foreach($days[$k] as $mIndex => $m)
+        <tr data-day="{{$k}}">
+            @if($mIndex === 0)
+                <td rowspan="{{count($days[$k])}}">
+                    {{$k}}
+                </td>
+            @endif
+            <td>{{$m->time}}</td>
+            <td>
+                @if($m->label === 'BP')
+                    BP
+                @elseif($m->label === 'Wt. (lbs.)')
+                    Weight
+                @endif
+            </td>
+            <td>
+                @if($m->label === 'BP')
+                    {{ $m->sbp_mm_hg }}/{{ $m->dbp_mm_hg }}
+                @elseif($m->label === 'Wt. (lbs.)')
+                    {{ round(floatval($m->numeric_value), 2) }} lbs
+                @endif
+            </td>
+            <td>
+                @if($performerRole === 'MCP')
+                    <div class="width-100px">
+                        @if($m->has_been_stamped_by_mcp)
+                            <span class="text-secondary text-sm">
+                                <i class="fa fa-check"></i>
+                                Stamped
+                            </span>
+                        @else
+                            <div moe relative>
+                                <a href="#" start show>Stamp</a>
+                                <form url="/api/measurement/stamp" right class="width-300px">
+                                    <input type="hidden" name="uid" value="{{$m->measurement_uid}}">
+                                    <p class="mb-1">Stamp this measurement?</p>
+                                    <div class="mt-2 bg-light border p-2">
+                                        <div class="mb-1 text-secondary">{{ $m->label }}</div>
+                                        <div class="font-weight-bold mb-1">
+                                            @if($m->label === 'BP')
+                                                {{ round($m->sbp_mm_hg, 2) }}/{{ round($m->dbp_mm_hg, 2) }} mmHg
+                                                <span class="font-weight-normal d-inline-block pl-2">Pulse:</span>
+                                                {{ $m->value_pulse }} {{ $m->value_irregular === 0?'Irregular':'' }} {{ $m->value_irregular === 1?'Regular':'' }}
+                                            @elseif($m->label === 'Wt. (lbs.)')
+                                                {{ round($m->numeric_value, 2) }} lbs
+                                            @else
+                                                {{ $m->value }}
+                                            @endif
+                                        </div>
+                                        <div class="text-sm">
+                                            {{ $m->date }} {{ $m->time }} EST
+                                        </div>
+                                    </div>
+                                    <!--<div class="mb-2 border border-info p-2 mt-2 bg-light">
+                                        <span>I have had interactive communication with <b>{{$patient->displayName()}}</b> during this care month.</span>
+                                        <div class="d-flex border-top mt-2">
+                                            <label class="mt-2 mb-0 d-inline-flex align-items-center mr-3">
+                                                <input type="radio" class="mr-2" name="communicatedToPatient" value="true" required>
+                                                <span>Yes</span>
+                                            </label>
+                                            <label class="mt-2 mb-0 d-inline-flex align-items-center">
+                                                <input type="radio" class="mr-2" name="communicatedToPatient" value="false" checked required>
+                                                <span>No</span>
+                                            </label>
+                                        </div>
+                                    </div>-->
+                                    <div class="mb-2">
+                                        <label class="mb-1 text-secondary text-sm">Memo</label>
+                                        <textarea class="form-control form-control-sm" name="memo"></textarea>
+                                    </div>
+                                    <div class="form-group m-0">
+                                        <button submit class="btn btn-primary btn-sm mr-2">Submit</button>
+                                        <button cancel class="btn btn-default border btn-sm mr-2">Cancel</button>
+                                    </div>
+                                </form>
+                            </div>
+                        @endif
+                    </div>
+                @elseif($performerRole === 'NON-HCP')
+                    <div class="width-100px">
+                        @if($m->has_been_stamped_by_non_hcp)
+                            <span class="text-secondary text-sm">
+                                <i class="fa fa-check"></i>
+                                Stamped
+                            </span>
+                        @else
+                            <div moe relative>
+                                <a href="#" start show>Stamp</a>
+                                <form url="/api/measurement/stamp" right class="width-300px">
+                                    <input type="hidden" name="uid" value="{{$m->measurement_uid}}">
+                                    <p class="mb-1">Stamp this measurement?</p>
+                                    <div class="mt-2 bg-light border p-2">
+                                        <div class="mb-1 text-secondary">{{ $m->label }}</div>
+                                        <div class="font-weight-bold mb-1">
+                                            @if($m->label === 'BP')
+                                                {{ round($m->sbp_mm_hg, 2) }}/{{ round($m->dbp_mm_hg, 2) }} mmHg
+                                                <span class="font-weight-normal d-inline-block pl-2">Pulse:</span>
+                                                {{ $m->value_pulse }} {{ $m->value_irregular === 0?'Irregular':'' }} {{ $m->value_irregular === 1?'Regular':'' }}
+                                            @elseif($m->label === 'Wt. (lbs.)')
+                                                {{ round($m->numeric_value, 2) }} lbs
+                                            @else
+                                                {{ $m->value }}
+                                            @endif
+                                        </div>
+                                        <div class="text-sm">
+                                            {{ $m->date }} {{ $m->time }} EST
+                                        </div>
+                                    </div>
+                                    <div class="mb-2">
+                                        <label class="mb-1 text-secondary text-sm">Memo</label>
+                                        <textarea class="form-control form-control-sm" name="memo"></textarea>
+                                    </div>
+                                    <div class="form-group m-0">
+                                        <button submit class="btn btn-primary btn-sm mr-2">Submit</button>
+                                        <button cancel class="btn btn-default border btn-sm mr-2">Cancel</button>
+                                    </div>
+                                </form>
+                            </div>
+                        @endif
+                    </div>
+                @endif
+            </td>
+            @if($mIndex === 0)
+                <td rowspan="{{count($days[$k])}}" class="px-2">
+                    @if($performerRole === 'ADMIN')
+                        <div class="my-1">
+                            <span class="text-secondary">MCP Unstamped:</span>
+                            <b class="{{count($unstampedMCP[$k]) ? '' : 'text-secondary font-weight-normal'}}">{{count($unstampedMCP[$k])}}</b>
+                        </div>
+                        <div class="">
+                            <span class="text-secondary">Non-HCP Unstamped:</span>
+                            <b class="{{count($unstampedNonHCP[$k]) ? '' : 'text-secondary font-weight-normal'}}">{{count($unstampedNonHCP[$k])}}</b>
+                        </div>
+                    @elseif($performerRole === 'MCP')
+                        @if(count($unstampedMCP[$k]))
+                            <div moe relative>
+                                <a href="#" start show class="mcp-stamp-all">
+                                    Stamp All
+                                </a>
+                                <form url="/api/measurement/bulkStamp" class="width-300px">
+                                    <input type="hidden" name="clientUid" value="{{$patient->uid}}">
+                                    <input type="hidden" class="measurements-uids" value="{{implode('|', $unstampedMCP[$k])}}">
+                                    <p class="mb-2 font-weight-bold">Stamp all measurements?</p>
+                                    <!--<div class="mb-2 border border-info p-2 mt-2 bg-light width-300px">
+                                        <span>I have had interactive communication with <b>{{$patient->displayName()}}</b> during this care month.</span>
+                                        <div class="d-flex border-top mt-2">
+                                            <label class="mt-2 mb-0 d-inline-flex align-items-center mr-3">
+                                                <input type="radio" class="mr-2" name="communicatedToPatient" value="true" required>
+                                                <span>Yes</span>
+                                            </label>
+                                            <label class="mt-2 mb-0 d-inline-flex align-items-center">
+                                                <input type="radio" class="mr-2" name="communicatedToPatient" value="false" checked required>
+                                                <span>No</span>
+                                            </label>
+                                        </div>
+                                    </div>-->
+                                    <?php
+                                    $dayTotalMinutes = 0;
+                                    foreach($m->entries as $entry) {
+                                        $dayTotalMinutes += round($entry->time_in_seconds / 60);
+                                    }
+                                    $autoCheckEntry = false;
+                                    $autoCheckEntry = ($dayTotalMinutes < 2 && $patient->is_enrolled_in_rm);
+                                    ?>
+                                    <div class="p-2 border border-info bg-light mb-2">
+                                        <div class="mb-2">
+                                            <label class="mb-1 text-secondary text-sm">Memo</label>
+                                            <textarea class="form-control form-control-sm" name="entryMemo">measurements within range</textarea>
+                                        </div>
+                                        <div class="">
+                                            <label class="mb-0 d-flex align-items-center">
+                                                <input type="checkbox" class="shouldAddEntry mr-2 my-0" {{$autoCheckEntry ? 'checked' : ''}}>
+                                                <span>Add Time Entry</span>
+                                            </label>
+                                        </div>
+                                        <div class="if-adding-time-entry pt-2 d-none">
+                                            <div class="mb-2">
+                                                <label class="mb-1 text-secondary text-sm">Minutes</label>
+                                                <input type="number" min="1" max="15"
+                                                       class="form-control form-control-sm w-100" name="entryNumberOfMinutes"
+                                                       value="1" placeholder="Time (mins.)">
+                                            </div>
+                                            <div class="mb-2">
+                                                <label class="mb-1 text-secondary text-sm">Date</label>
+                                                <input type="date" class="form-control form-control-sm w-100"
+                                                       name="entryDate"
+                                                       value="{{date('Y-m-d', strtotime($k))}}">
+                                            </div>
+                                            <!--<div class="">
+                                                <label class="mb-0 d-flex align-items-baseline">
+                                                    <input type="checkbox" class="hasAnyoneInteractedWithClientAboutRmOutsideNote mr-2 mt-1">
+                                                    <span>Has anyone interacted with client about rm outside note?</span>
+                                                </label>
+                                            </div>-->
+                                        </div>
+                                    </div>
+                                    <div class="form-group m-0">
+                                        <button type="button" class="btn btn-primary btn-sm mr-2 btn-bulk-stamp">Submit</button>
+                                        <button cancel class="btn btn-default border btn-sm mr-2">Cancel</button>
+                                    </div>
+                                </form>
+                            </div>
+                        @else
+                            <span class="text-secondary text-sm d-flex align-items-center">
+                                <i class="fa fa-check mr-2"></i>
+                                <span>All Stamped</span>
+                            </span>
+                        @endif
+                    @elseif($performerRole === 'NON-HCP')
+                        @if(count($unstampedNonHCP[$k]))
+                            <div moe relative>
+                                <a href="#" start show class="">
+                                    Stamp All
+                                </a>
+                                <form url="/api/measurement/bulkStamp" class="width-300px">
+                                    <input type="hidden" name="clientUid" value="{{$patient->uid}}">
+                                    <input type="hidden" class="measurements-uids" value="{{implode('|', $unstampedNonHCP[$k])}}">
+                                    <p class="mb-2 font-weight-bold">Stamp all measurements?</p>
+                                    <?php
+                                    $dayTotalMinutes = 0;
+                                    foreach($m->entries as $entry) {
+                                        $dayTotalMinutes += round($entry->time_in_seconds / 60);
+                                    }
+                                    $autoCheckEntry = false;
+                                    $autoCheckEntry = ($dayTotalMinutes < 2 && $patient->is_enrolled_in_rm);
+                                    ?>
+                                    <div class="p-2 border border-info bg-light mb-2">
+                                        <div class="mb-2">
+                                            <label class="mb-1 text-secondary text-sm">Memo</label>
+                                            <textarea class="form-control form-control-sm" name="entryMemo"></textarea>
+                                        </div>
+                                        <div class="">
+                                            <label class="mb-0 d-flex align-items-center">
+                                                <input type="checkbox" class="shouldAddEntry mr-2 my-0" {{$autoCheckEntry ? 'checked' : ''}}>
+                                                <span>Add Time Entry</span>
+                                            </label>
+                                        </div>
+                                        <div class="if-adding-time-entry pt-2 d-none">
+                                            <div class="mb-2">
+                                                <label class="mb-1 text-secondary text-sm">Minutes</label>
+                                                <input type="number" min="1" max="15"
+                                                       class="form-control form-control-sm w-100" name="entryNumberOfMinutes"
+                                                       value="1" placeholder="Time (mins.)">
+                                            </div>
+                                            <div class="mb-2">
+                                                <label class="mb-1 text-secondary text-sm">Date</label>
+                                                <input type="date" class="form-control form-control-sm w-100"
+                                                       name="entryDate"
+                                                       value="{{date('Y-m-d', strtotime($k))}}">
+                                            </div>
+                                            <!--<div class="">
+                                                <label class="mb-0 d-flex align-items-baseline">
+                                                    <input type="checkbox" class="hasAnyoneInteractedWithClientAboutRmOutsideNote mr-2 mt-1">
+                                                    <span>Has anyone interacted with client about rm outside note?</span>
+                                                </label>
+                                            </div>-->
+                                        </div>
+                                    </div>
+                                    <div class="form-group m-0">
+                                        <button type="button" class="btn btn-primary btn-sm mr-2 btn-bulk-stamp">Submit</button>
+                                        <button cancel class="btn btn-default border btn-sm mr-2">Cancel</button>
+                                    </div>
+                                </form>
+                            </div>
+                        @else
+                            <span class="text-secondary text-sm d-flex align-items-center">
+                                <i class="fa fa-check mr-2"></i>
+                                <span>All Stamped</span>
+                            </span>
+                        @endif
+                    @endif
+                </td>
+                <td rowspan="{{count($days[$k])}}" class="px-2">
+                    @foreach($m->entries as $entry)
+                        <div class="my-1 d-flex align-items-baseline flex-nowrap">
+                            <b class="mr-2">{{round($entry->time_in_seconds / 60)}}m</b>
+                            <span class="text-secondary mr-2 text-nowrap text-sm">({{friendly_date_time($entry->created_at)}})</span>
+                            <span class="text-secondary inline-html-container flex-grow-1">{!! $entry->content_text !!}</span>
+                        </div>
+                        @if($pro->pro_type === 'ADMIN')
+                            <div class="mt-1">
+                                <span class="text-secondary">Pro: </span>
+                                {{$entry->pro ? $entry->pro->displayName() : '-'}}
+                            </div>
+                        @endif
+                    @endforeach
+                    @if($performerRole === 'MCP' || $performerRole === 'NON-HCP')
+                        <div moe large relative>
+                            <a start show class="py-0 mb-3 text-sm">+ Add</a>
+                            <form url="/api/careMonthEntry/createForRm" right>
+                                <input type="hidden" name="careMonthUid" value="{{ $careMonth->uid }}">
+                                <div class="mb-2">
+                                    <div class="row">
+                                        <div class="col-4 pr-0">
+                                            <select class="form-control form-control-sm w-100" name="proUid" provider-search data-pro-uid="{{ @$pro->uid }}"
+                                                    required>
+                                                <option value="">-- Select Pro --</option>
+                                            </select>
+                                        </div>
+                                        <div class="col-4 pr-0">
+                                            <?php
+                                            $sD = strtotime($careMonth->start_date);
+                                            $y = date('Y', $sD);
+                                            $m0 = date('m', $sD);
+                                            $d = date('t', $sD);
+                                            $yCurrent = date('Y');
+                                            $mCurrent = date('m');
+                                            ?>
+                                            <input autofocus type="date" min="{{ date($y . '-' . $m0 . '-01') }}" max="{{ date($y . '-' . $m0 . '-' . $d) }}"
+                                                   value="{{$m->dateYMD}}"
+                                                   class="form-control form-control-sm w-100" name="effectiveDate"
+                                                   placeholder="Effective Date" required>
+                                        </div>
+                                        <div class="col-4">
+                                            <input type="number" min="0" max="15" class="form-control form-control-sm w-100 cm-time-value" name="timeInMinutes"
+                                                   value="1" placeholder="Time (mins.)" required>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="mb-2">
+                                    <div class="row">
+                                        <div class="col-12">
+                                            <div cm-rte data-content="Reviewed/managed patient measurements" data-name="contentText"></div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="d-flex align-items-center">
+                                    <button class="btn btn-sm btn-primary mr-2" submit>Save</button>
+                                    <button class="btn btn-sm btn-default mr-2 border" cancel>Cancel</button>
+                                </div>
+                            </form>
+                        </div>
+                    @endif
+                </td>
+            @endif
+        </tr>
+        @endforeach
+    @endforeach
+    </tbody>
+</table>
+<div class="my-3 borde d-none cm-tab cm-tab" tab-key="measurements-graph">
+    @include('app.patient.care-month._vitals_graph')
+</div>
+<div class="my-3 border p-3 d-none cm-tab" tab-key="bmi-summary">
+    <div class="d-flex align-items-baseline mb-2">
+        <b>BMI/Weight Management</b>
+        <span class="mx-2 text-secondary">|</span>
+        @include('app.patient.vitals-settings.bmi-management-form')
+    </div>
+    @include('app.patient.vitals-settings.bmi-management-summary')
+</div>
+<div class="my-3 border p-3 d-none cm-tab" tab-key="bp-summary">
+    <div class="d-flex align-items-baseline mb-2">
+        <b>BP Management</b>
+        <span class="mx-2 text-secondary">|</span>
+        @include('app.patient.vitals-settings.bp-management-form')
+    </div>
+    @include('app.patient.vitals-settings.bp-management-summary')
+</div>
+</div>
+<script>
+    (function() {
+        function init() {
+
+            $('.btn-stamp')
+                .off('click')
+                .on('click', function () {
+                    let form = $(this).closest('form');
+                    if(!form[0].checkValidity()) {
+                        form[0].reportValidity();
+                        return false;
+                    }
+
+                    showMask();
+
+                    let payload = {
+                        uid: form.find('[name="uid"]').val(),
+                        memo: form.find('[name="memo"]').val(),
+                    }
+
+                    $.ajax({
+                        url: form.attr('url'),
+                        type:"POST",
+                        data: JSON.stringify(payload),
+                        contentType:"application/json; charset=utf-8",
+                        dataType:"json",
+                        success: function(_data) {
+                            if(!hasResponseError(_data)) {
+                                if(form.closest('.stag-popup').length) {
+                                    refreshDynamicStagPopup();
+                                }
+                                else {
+                                    fastReload();
+                                }
+                            }
+                        }
+                    }).then(() => {
+                        hideMask();
+                    });
+                    return false;
+                });
+
+            $('.btn-bulk-stamp')
+                .off('click')
+                .on('click', function () {
+                    let form = $(this).closest('form');
+                    if(!form[0].checkValidity()) {
+                        form[0].reportValidity();
+                        return false;
+                    }
+
+                    showMask();
+
+                    let payload = {
+                        clientUid: '{{$patient->uid}}',
+                        toStamp: form.find('.measurements-uids').val().split('|').map(_x => {
+                            return {
+                                measurementUid: _x,
+                                memo: null,
+                                detailJson: null
+                            }
+                        }),
+                        shouldAddEntry: form.find('.shouldAddEntry').prop('checked') ? 1 : 0,
+                        entryDate: form.find('[name="entryDate"]').val(),
+                        entryMemo: form.find('[name="entryMemo"]').val(),
+                        entryNumberOfMinutes: form.find('[name="entryNumberOfMinutes"]').val() ? +(form.find('[name="entryNumberOfMinutes"]').val()) : 0,
+                        hasAnyoneInteractedWithClientAboutRmOutsideNote: form.find('.hasAnyoneInteractedWithClientAboutRmOutsideNote').prop('checked') ? 1 : 0,
+                    }
+
+                    $.ajax({
+                        url: form.attr('url'),
+                        type:"POST",
+                        data: JSON.stringify(payload),
+                        contentType:"application/json; charset=utf-8",
+                        dataType:"json",
+                        success: function(_data) {
+                            if(!hasResponseError(_data)) {
+                                if(form.closest('.stag-popup').length) {
+                                    refreshDynamicStagPopup();
+                                }
+                                else {
+                                    fastReload();
+                                }
+                            }
+                        }
+                    }).then(() => {
+                        hideMask();
+                    });
+                    return false;
+                });
+
+            $('.shouldAddEntry')
+                .off('change')
+                .on('change', function () {
+                    let form = $(this).closest('form');
+                    if(this.checked) {
+                        form.find('.if-adding-time-entry').removeClass('d-none');
+                    }
+                    else {
+                        form.find('.if-adding-time-entry').addClass('d-none');
+                    }
+                    return false;
+                })
+                .trigger('change');
+
+            let currentStampAllRow = null;
+
+            function autoClickNextFirstAll() {
+                let link = $('#rpm-work-matrix .mcp-stamp-all').first();
+                if(link.length) {
+                    currentStampAllRow = link.closest('tr');
+                    $('#rpm-work-matrix tr[data-day="' + currentStampAllRow.attr('data-day') + '"]').addClass('bg-warning-mellow');
+                    link.trigger('click');
+                }
+            }
+
+            runMCInitializer('vitalsGraph');
+
+            window.setTimeout(function() {
+                autoClickNextFirstAll();
+            }, 0);
+
+        }
+        addMCInitializer('rpm-work-matrix', init, '#rpm-work-matrix');
+    }).call(window);
+</script>

+ 1 - 172
resources/views/app/patient/care-month/dashboard.blade.php

@@ -229,178 +229,7 @@
                                 </div>
 
                                 <div class="d-none cm-tab border pr-4" tab-key="measurements-graph">
-                                    <link href="/c3/c3.min.css" rel="stylesheet">
-                                    <script src="/c3/d3.v5.min.js" charset="utf-8"></script>
-                                    <script src="/c3/c3.min.js"></script>
-
-                                    <div id="vitalsGraphComponent" class="stag-chart mb-4 pt-3">
-                                        <h4 class="font-weight-bold mb-1 text-secondary font-size-14 text-center">Blood Pressure</h4>
-                                        <div id="bp-chart" class="mb-4">BP Graph</div>
-                                        <hr class="my-3">
-                                        <h4 class="font-weight-bold mb-1 text-secondary font-size-14 text-center">Weight</h4>
-                                        <div id="weight-chart">Weight Graph</div>
-                                    </div>
-
-                                    <?php
-                                    $dates = [];
-                                    $startDate = $careMonth->start_date;
-                                    $nextMonthFirstDay = date_format(date_add(date_create($startDate), date_interval_create_from_date_string("1 month")), 'Y-m-d');
-
-                                    $nextDay = $startDate;
-                                    while ($nextDay !== $nextMonthFirstDay) {
-                                        $dates[] = $nextDay;
-                                        $nextDay = date_format(date_add(date_create($nextDay), date_interval_create_from_date_string('1 day')), 'Y-m-d');
-                                    }
-
-                                    /** @var \App\Models\Client $patient */
-
-                                    // BP
-                                    $bpMeasurements = $patient->getNonZeroBpMeasurements->toArray();
-                                    $weightMeasurements = $patient->getNonZeroWeightMeasurements->toArray();
-
-                                    $bpData = [];
-                                    $weightData = [];
-
-                                    for ($i=0; $i<count($dates); $i++) {
-
-                                        $date = $dates[$i];
-
-                                        // bp
-                                        $bp = array_filter($bpMeasurements, function($_measurement) use ($date) {
-                                            return $_measurement['effective_date'] === $date;
-                                        });
-                                        if(count($bp)) {
-                                            $bp = array_values($bp);
-                                            $bp = $bp[count($bp) - 1];
-                                        }
-                                        else {
-                                            $bp = null;
-                                        }
-
-
-                                        if ($bp) {
-                                            $bpData[] = [
-                                                "date" => $date,
-                                                "sbp" => $bp["sbp_mm_hg"],
-                                                "dbp" => $bp["dbp_mm_hg"]
-                                            ];
-                                        }
-
-                                        // weight
-                                        $weight = array_filter($weightMeasurements, function($_measurement) use ($date) {
-                                            return $_measurement['effective_date'] === $date;
-                                        });
-                                        if(count($weight)) {
-                                            $weight = array_values($weight);
-                                            $weight = $weight[count($weight) - 1];
-                                            $weightData[] = [
-                                                "date" => $date,
-                                                "weight" => $weight["numeric_value"]
-                                            ];
-                                        }
-
-                                    }
-
-                                    $bpDates = [];
-                                    $sbpValues = [];
-                                    $dbpValues = [];
-                                    for ($i = 0; $i < count($bpData); $i++) {
-                                        $bpDates[] = $bpData[$i]['date'];
-                                        $sbpValues[] = $bpData[$i]['sbp'];
-                                        $dbpValues[] = $bpData[$i]['dbp'];
-                                    }
-
-                                    $weightDates = [];
-                                    $weightValues = [];
-                                    for ($i = 0; $i < count($weightData); $i++) {
-                                        $weightDates[] = $weightData[$i]['date'];
-                                        $weightValues[] = $weightData[$i]['weight'];
-                                    }
-
-                                    ?>
-
-                                    <script>
-                                        (function() {
-                                            function init() {
-                                                bpChart();
-                                                weightChart();
-                                            }
-                                            function bpChart() {
-                                                window.vgBPChart = c3.generate({
-                                                    bindto: '#bp-chart',
-                                                    data: {
-                                                        x: 'x',
-                                                        // xFormat: '%Y%m%d', // 'xFormat' can be used as custom format of 'x'
-                                                        columns: [
-                                                            ['x', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $bpDates)) ?>],
-                                                            ['Systolic BP', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $sbpValues)) ?>],
-                                                            ['Diastolic BP', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $dbpValues)) ?>]
-                                                        ]
-                                                    },
-                                                    axis: {
-                                                        x: {
-                                                            type: 'timeseries',
-                                                            tick: {
-                                                                format: '%Y-%m-%d',
-                                                                multiline: true,
-                                                                fit: true,
-                                                                rotate: -45
-                                                            },
-                                                        },
-                                                        y: {
-                                                            show: true,
-                                                            label: {
-                                                                text: 'Blood Pressure (mmHg)',
-                                                                position: 'outer-middle'
-                                                            },
-                                                            min: 60,
-                                                            max: 220
-                                                        },
-                                                    },
-                                                    regions: [
-                                                        {axis: 'y', start: 100, end: 130, class: 'safe-region', label: 'Safe Systolic BP: 100 to 130 mmHg'},
-                                                        {axis: 'y', start: 60, end: 90, class: 'safe-region', label: 'Safe Diastolic BP: 60 to 90 mmHg'}
-                                                    ]
-                                                });
-                                            }
-                                            function weightChart() {
-                                                window.vgWtChart = c3.generate({
-                                                    bindto: '#weight-chart',
-                                                    data: {
-                                                        x: 'x',
-                                                        columns: [
-                                                            ['x', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $weightDates)) ?>],
-                                                            ['Weight', <?= implode(", ", array_map(function($_x) { return "'" . $_x . "'"; }, $weightValues)) ?>]
-                                                        ]
-                                                    },
-                                                    axis: {
-                                                        x: {
-                                                            type: 'timeseries',
-                                                            tick: {
-                                                                format: '%Y-%m-%d',
-                                                                multiline: true,
-                                                                fit: true,
-                                                                rotate: -45
-                                                            },
-                                                        },
-                                                        y: {
-                                                            show: true,
-                                                            label: {
-                                                                text: 'Weight (lbs)',
-                                                                position: 'outer-middle'
-                                                            },
-                                                            min: 70,
-                                                            max: 250
-                                                        },
-                                                    },
-                                                    regions: [
-                                                        // {axis: 'y', start: 100, end: 140, class: 'safe-region', label: 'Safe Weight: 100 to 140 lbs'},
-                                                    ]
-                                                });
-                                            }
-                                            addMCInitializer('vitalsGraph', init, '#vitalsGraphComponent');
-                                        }).call(window);
-                                    </script>
+                                    @include('app.patient.care-month._vitals_graph')
                                 </div>
                             </div>
                             <div class="col-3 px-0">

+ 12 - 2
resources/views/app/practice-management/remote-monitoring.blade.php

@@ -6,7 +6,7 @@
 
         <div class="card">
 
-            <div class="card-header px-2 py-2 d-flex align-items-center">
+            <div class="card-header px-2 py-2 d-flex align-items-baseline">
                 <span class="mr-4">
                     <span class="font-size-14">Remote Monitoring</span>
                     <i class="fas fa-arrow-right text-sm mx-1"></i>
@@ -19,6 +19,16 @@
                 $m = +$mStr;
                 $y = +$yStr;
                 ?>
+                <a href="{{ route('mcp.rpm_work_matrix') }}?m={{$mStr}}&y={{$yStr}}"
+                   native target="_blank"
+                   open-in-stag-popup
+                   update-parent
+                   popup-style="tall overflow-visible"
+                   class="ml-2 font-weight-bold"
+                   mc-initer="rpm-work-matrix"
+                   title="RPM Work Matrix">
+                    RPM Work Matrix
+                </a>
                 <form class="ml-auto d-inline-flex flex-nowrap align-items-center" action="" method="GET">
                     <span class="mr-2">Month</span>
                     <select class="form-control form-control-sm min-width-unset width-100px mr-3" name="m"
@@ -186,7 +196,7 @@
                                                    popup-style="tall overflow-visible"
                                                    class="ml-2 text-sm"
                                                    mc-initer="cm-matrix-{{$oPatient->id}}"
-                                                   title="Measurements Pending Stamping">
+                                                   title="{{$oPatient->displayName(true)}}: Measurements Pending Stamping">
                                                     Stamp
                                                 </a>
                                             @endif

+ 3 - 0
routes/web.php

@@ -108,6 +108,9 @@ Route::middleware('pro.auth')->group(function () {
         Route::get('measurements-pending-stamping', 'McpController@measurements_pending_stamping')->name('measurements_pending_stamping');
         Route::get('measurements-pending-stamping-in-care-month', 'McpController@measurements_pending_stamping_in_care_month')->name('measurements_pending_stamping_in_care_month');
 
+        // rpm work matrix - latest patient with unstamped measurements
+        Route::get('rpm-work-matrix', 'McpController@rpm_work_matrix')->name('rpm_work_matrix');
+
         Route::get('measurements-mass-stamping', 'McpController@measurements_mass_stamping')->name('measurements_mass_stamping');
 
     });