|
@@ -1,10 +1,17 @@
|
|
|
@extends ('layouts/template')
|
|
|
|
|
|
@section('content')
|
|
|
-<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>
|
|
|
+<style>
|
|
|
+ #managementStatsComponent h5 {
|
|
|
+ font-size: 19px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
|
|
|
+ #managementStatsComponent span.count {
|
|
|
+ font-size: 47px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+</style>
|
|
|
<div class="p-3 mcp-theme-1" id="patients-list">
|
|
|
<div class="card">
|
|
|
|
|
@@ -17,142 +24,231 @@
|
|
|
<div class="p-3">
|
|
|
@include('app.admin.management-stats.filters')
|
|
|
</div>
|
|
|
- <div id="managementStatsComponent">
|
|
|
+ <div id="managementStatsComponent" v-cloak>
|
|
|
<div class="row">
|
|
|
<div class="col-md-6 mb-2">
|
|
|
<div class="card">
|
|
|
- <div class="card-header">Who are the clients we paid to acquire?</div>
|
|
|
+ <div class="card-header">Overview</div>
|
|
|
+ <div class="card-body bg-light">
|
|
|
+ <div class="row">
|
|
|
+ <div class="col-md-6">
|
|
|
+ <div class="bg-white p-3 border rounded">
|
|
|
+ <div class="d-flex flex-column">
|
|
|
+ <h5>Acquired Clients</h5>
|
|
|
+ <span class="count">@{{ resultMap.AcquiredClients.length }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-6">
|
|
|
+ <div class="bg-white p-3 border rounded">
|
|
|
+ <div class="d-flex flex-column">
|
|
|
+ <h5>Total Billed Amount</h5>
|
|
|
+ <span class="count">@{{ formatAmount(resultMap.TotalBilledAmount[0].sum) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-6 mb-2">
|
|
|
+ <div class="card">
|
|
|
+ <div class="card-header">Active Clients by Month</div>
|
|
|
+ <div class="card-body">
|
|
|
+ <canvas id="ActiveClientsByMonth"></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-6 mb-2">
|
|
|
+ <div class="card">
|
|
|
+ <div class="card-header">Active Pros by Month</div>
|
|
|
<div class="card-body">
|
|
|
- <div id="clientsWePaidToAcquire"></div>
|
|
|
+ <canvas id="ActiveProsByMonth"></canvas>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="col-md-6 mb-2">
|
|
|
<div class="card">
|
|
|
- <div class="card-header">Active Clients</div>
|
|
|
+ <div class="card-header">Note Count By Month</div>
|
|
|
<div class="card-body">
|
|
|
- <div id="activeClients"></div>
|
|
|
+ <canvas id="NoteCountByMonth"></canvas>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="col-md-6 mb-2">
|
|
|
<div class="card">
|
|
|
- <div class="card-header">Active HCPs</div>
|
|
|
+ <div class="card-header">Cpt Codes Billed</div>
|
|
|
<div class="card-body">
|
|
|
- <div id="activeHCPs"></div>
|
|
|
+ <canvas id="CptCodesBilled"></canvas>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="col-md-6 mb-2">
|
|
|
<div class="card">
|
|
|
- <div class="card-header">CPT Codes Billed</div>
|
|
|
+ <div class="card-header">Cpt Codes Billed By Month</div>
|
|
|
<div class="card-body">
|
|
|
- <div id="cptCodesBilled"></div>
|
|
|
+ <canvas id="CptCodesBilledByMonth"></canvas>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
<script>
|
|
|
(function() {
|
|
|
+ const formatter = new Intl.NumberFormat('en-US', {
|
|
|
+ style: 'currency',
|
|
|
+ currency: 'USD',
|
|
|
+ });
|
|
|
+
|
|
|
function init() {
|
|
|
new Vue({
|
|
|
el: '#managementStatsComponent',
|
|
|
delimiters: ['@{{', '}}'],
|
|
|
data: {
|
|
|
- clientsWePaidToAcquire: <?= json_encode($clientsWePaidToAcquire) ?>,
|
|
|
- activeClients: <?= json_encode($activeClients) ?>,
|
|
|
- activeHCPs: <?= json_encode($activeHCPs) ?>,
|
|
|
- cptCodesBilled: <?= json_encode($cptCodesBilled) ?>
|
|
|
+ view: 'CHARTS',
|
|
|
+ resultMap: <?= json_encode($resultMap) ?>
|
|
|
},
|
|
|
methods: {
|
|
|
- dateValueDataObjectBarGraph: function(data, dateLabel, valueLabel, chartID, yAxisLegend) {
|
|
|
- var dates = [];
|
|
|
- var values = [];
|
|
|
+ formatAmount: function(amount){
|
|
|
+ if(!amount) return 0;
|
|
|
+ return formatter.format(amount);
|
|
|
+ },
|
|
|
+ drawDateValueBarGraph: function(data, date_prop, value_prop, canvasID, datasetLabel, dateFormat, barColor) {
|
|
|
+ var xAxisValues = [];
|
|
|
+ var yAxisValues = [];
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
|
var d = data[i];
|
|
|
- dates.push(d[dateLabel]);
|
|
|
- values.push(d[valueLabel]);
|
|
|
+ var xAxisValue = d[date_prop];
|
|
|
+ if (dateFormat) {
|
|
|
+ xAxisValue = moment(xAxisValue, 'YYYY-MM-DD').format(dateFormat);
|
|
|
+ }
|
|
|
+ xAxisValues.push(xAxisValue);
|
|
|
+ yAxisValues.push(d[value_prop]);
|
|
|
}
|
|
|
- var elementID = chartID;
|
|
|
- var myData = {};
|
|
|
-
|
|
|
- myData.x = 'x';
|
|
|
- myData.xFormat = "%Y-%m-%d";
|
|
|
- myData.type = 'bar';
|
|
|
- myX = dates;
|
|
|
- myY = values;
|
|
|
- myX.splice(0, 0, 'x');
|
|
|
- myY.splice(0, 0, yAxisLegend);
|
|
|
- myData.columns = [];
|
|
|
- myData.columns.push(myX);
|
|
|
- myData.columns.push(myY);
|
|
|
- var chart = c3.generate({
|
|
|
- bindto: elementID,
|
|
|
- data: myData,
|
|
|
- size: {
|
|
|
- height: 480,
|
|
|
- width: 400,
|
|
|
- },
|
|
|
- bar: {
|
|
|
- width: {
|
|
|
- ratio: 0.5
|
|
|
- }
|
|
|
+ const ctx = document.getElementById(canvasID);
|
|
|
+ new Chart(ctx, {
|
|
|
+ type: 'bar',
|
|
|
+ data: {
|
|
|
+ labels: xAxisValues,
|
|
|
+ datasets: [{
|
|
|
+ label: datasetLabel,
|
|
|
+ data: yAxisValues,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: barColor,
|
|
|
+ backgroundColor: barColor
|
|
|
+ }]
|
|
|
},
|
|
|
- axis: {
|
|
|
- x: {
|
|
|
- type: 'timeseries',
|
|
|
- tick: {
|
|
|
- format: "%b-%d"
|
|
|
+ options: {
|
|
|
+ scales: {
|
|
|
+ y: {
|
|
|
+ beginAtZero: true
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
+ }
|
|
|
}
|
|
|
- })
|
|
|
+ });
|
|
|
},
|
|
|
- initClientsWePaidToAcquire: function() {
|
|
|
- var data = this.clientsWePaidToAcquire;
|
|
|
- this.dateValueDataObjectBarGraph(data, 'date_trunc', 'count', '#clientsWePaidToAcquire', 'Clients we paid to acquire');
|
|
|
-
|
|
|
+ initNoteCountByMonthGraph: function() {
|
|
|
+ var data = this.resultMap.NoteCountByMonth;
|
|
|
+ this.drawDateValueBarGraph(data, 'date_trunc', 'count', 'NoteCountByMonth', 'Note count by month', 'MMM/YYYY', '#17a2b8');
|
|
|
},
|
|
|
- initActiveClients: function() {
|
|
|
- var data = this.activeClients;
|
|
|
- this.dateValueDataObjectBarGraph(data, 'date_trunc', 'count', '#activeClients', 'Active Clients');
|
|
|
+ initActiveClientsByMonth: function() {
|
|
|
+ var data = this.resultMap.ActiveClientsByMonth;
|
|
|
+ this.drawDateValueBarGraph(data, 'date_trunc', 'count', 'ActiveClientsByMonth', 'Active Clients by month', 'MMM/YYYY');
|
|
|
},
|
|
|
- initActiveHCPs: function() {
|
|
|
- var data = this.activeHCPs;
|
|
|
- this.dateValueDataObjectBarGraph(data, 'date_trunc', 'count', '#activeHCPs', 'Active HCPs');
|
|
|
+ initActiveProsByMonth: function() {
|
|
|
+ var data = this.resultMap.ActiveProsByMonth;
|
|
|
+ this.drawDateValueBarGraph(data, 'date_trunc', 'count', 'ActiveProsByMonth', 'Active Pros by month', 'MMM/YYYY', '#65BF7A');
|
|
|
},
|
|
|
- initCpdCodesBilled: function() {
|
|
|
- var chart = c3.generate({
|
|
|
- bindto: '#cptCodesBilled',
|
|
|
- data: {
|
|
|
- columns: [
|
|
|
- ['99202', 30],
|
|
|
- ['99203', 3],
|
|
|
- ],
|
|
|
- type: 'bar'
|
|
|
- },
|
|
|
- bar: {
|
|
|
- width: {
|
|
|
- ratio: 0.5
|
|
|
+ initCptCodesBilled: function() {
|
|
|
+ var data = this.resultMap.CptCodesBilled;
|
|
|
+ this.drawDateValueBarGraph(data, 'cpt', 'count', 'CptCodesBilled', 'Cpt Codes Billed', null, '#ffc107');
|
|
|
+ },
|
|
|
+ initCptCodesBilledByMonth: function() {
|
|
|
+ var data = this.resultMap.CptCodesBilledByMonth;
|
|
|
+ var dateMap = {};
|
|
|
+ for (var i = 0; i < data.length; i++) {
|
|
|
+ var d = data[i];
|
|
|
+ if (!dateMap[d.date_trunc]) {
|
|
|
+ dateMap[d.date_trunc] = {
|
|
|
+ date: moment(d.date_trunc, 'YYYY-MM-DD').format('MMM/YYYY'),
|
|
|
+ cpts: {}
|
|
|
+ };
|
|
|
+ }
|
|
|
+ if (!dateMap[d.date_trunc].cpts[d.cpt]) {
|
|
|
+ dateMap[d.date_trunc].cpts[d.cpt] = null
|
|
|
+ }
|
|
|
+ dateMap[d.date_trunc].cpts[d.cpt] = d.count;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log({
|
|
|
+ dateMap
|
|
|
+ });
|
|
|
+ const LABELS = [];
|
|
|
+ const CPTS = {};
|
|
|
+ for (f in dateMap) {
|
|
|
+ var date = dateMap[f].date;
|
|
|
+ LABELS.push(date);
|
|
|
+ for (var c in dateMap[f].cpts) {
|
|
|
+ var cptValue = dateMap[f].cpts[c];
|
|
|
+ if (!CPTS[c]) {
|
|
|
+ CPTS[c] = [];
|
|
|
}
|
|
|
- },
|
|
|
- axis: {
|
|
|
- x: {
|
|
|
- type: 'category',
|
|
|
- tick: { centered: true},
|
|
|
+ CPTS[c].push(cptValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var CPT_DATASETS = [];
|
|
|
+ for (cpt in CPTS) {
|
|
|
+ var cptArray = CPTS[cpt];
|
|
|
+ CPT_DATASETS.push({
|
|
|
+ label: cpt,
|
|
|
+ backgroundColor: null,
|
|
|
+ data: cptArray
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ console.log({
|
|
|
+ LABELS,
|
|
|
+ CPTS,
|
|
|
+ CPT_DATASETS
|
|
|
+ });
|
|
|
+ var ctx = document.getElementById('CptCodesBilledByMonth');
|
|
|
+ var data = {
|
|
|
+ labels: LABELS,
|
|
|
+ datasets: CPT_DATASETS
|
|
|
+ };
|
|
|
+
|
|
|
+ var myBarChart = new Chart(ctx, {
|
|
|
+ type: 'bar',
|
|
|
+ data: data,
|
|
|
+ options: {
|
|
|
+ barValueSpacing: 20,
|
|
|
+ scales: {
|
|
|
+ yAxes: [{
|
|
|
+ ticks: {
|
|
|
+ min: 0,
|
|
|
+ }
|
|
|
+ }]
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
init: function() {
|
|
|
- this.initClientsWePaidToAcquire();
|
|
|
- this.initActiveClients();
|
|
|
- this.initActiveHCPs();
|
|
|
- this.initCpdCodesBilled();
|
|
|
+ console.log({
|
|
|
+ map: this.resultMap
|
|
|
+ });
|
|
|
+ this.initActiveClientsByMonth();
|
|
|
+ this.initActiveProsByMonth();
|
|
|
+ this.initNoteCountByMonthGraph();
|
|
|
+ this.initCptCodesBilled();
|
|
|
+ this.initCptCodesBilledByMonth();
|
|
|
},
|
|
|
},
|
|
|
mounted: function() {
|