Upload 2 files
Browse files- app.js +188 -106
- index.html +524 -237
app.js
CHANGED
|
@@ -2,64 +2,64 @@
|
|
| 2 |
HCP Segmentation Dashboard β Chart.js Unified Engine
|
| 3 |
All data from hcp_analysis_clean.parquet (191 columns)
|
| 4 |
============================================================ */
|
| 5 |
-
Chart.defaults.color='#64748b';Chart.defaults.borderColor='#e2e8f0';Chart.defaults.font.family="'Inter',sans-serif";Chart.defaults.font.size=13;
|
| 6 |
-
Chart.defaults.plugins.legend.labels.usePointStyle=true;Chart.defaults.plugins.legend.labels.pointStyle='circle';Chart.defaults.plugins.legend.labels.padding=20;
|
| 7 |
-
Chart.defaults.plugins.tooltip.backgroundColor='#1e293b';Chart.defaults.plugins.tooltip.padding=14;Chart.defaults.plugins.tooltip.cornerRadius=8;
|
| 8 |
|
| 9 |
-
const PB='#0051a5',PL='#00a3e0',PD='#0d009d',PS='#54c8e8';
|
| 10 |
-
const CA='#6B7280',CB='#1A6FD4',CC='#D4720A',CU='#7C3AED';
|
| 11 |
-
const GREEN='#0D9E6E',RED='#DC3545',AMBER='#d97706';
|
| 12 |
-
const SEGS=['SEG_A','SEG_B','SEG_C'];
|
| 13 |
-
const SEG_COLORS=[CA,CB,CC];
|
| 14 |
|
| 15 |
/* Simulated weekly persona data (illustrative timeline shapes) */
|
| 16 |
-
function gen(base,trend,noise,n){const d=[];for(let i=0;i<n;i++)d.push(Math.max(0,Math.round((base+trend*i+(Math.random()-0.5)*noise)*10)/10));return d;}
|
| 17 |
-
const W=20,wk=Array.from({length:W},(_,i)=>`W${(i+1)*4}`);
|
| 18 |
-
const P={a:{trx:gen(12,0,1.5,W),eng:gen(2,0.02,0.5,W),nrx:gen(1,0,0.5,W)},b:{trx:gen(8,0.4,2,W),eng:gen(5,0.3,1,W),nrx:gen(3,0.25,0.8,W)},c:{trx:gen(5,0.15,1.5,W),eng:gen(3,0.1,1.2,W),nrx:gen(2,0.08,0.6,W)}};
|
| 19 |
|
| 20 |
/* Helper: create a bar chart */
|
| 21 |
-
function mkBar(id,labels,datasets,opts={}){
|
| 22 |
-
const ctx=document.getElementById(id);if(!ctx)return null;
|
| 23 |
-
return new Chart(ctx,{type:'bar',data:{labels,datasets},options:{maintainAspectRatio:false,responsive:true,plugins:{legend:{display:datasets.length>1,position:'bottom'},...(opts.plugins||{})},scales:{y:{beginAtZero:true,grid:{color:'#f1f5f9'},...(opts.y||{})},x:{grid:{display:false},...(opts.x||{})}},...(opts.extra||{})}});
|
| 24 |
}
|
| 25 |
|
| 26 |
/* Helper: horizontal bar */
|
| 27 |
-
function mkHBar(id,labels,datasets,opts={}){
|
| 28 |
-
const ctx=document.getElementById(id);if(!ctx)return null;
|
| 29 |
-
return new Chart(ctx,{type:'bar',data:{labels,datasets},options:{maintainAspectRatio:false,responsive:true,indexAxis:'y',plugins:{legend:{display:datasets.length>1,position:'bottom'}},scales:{x:{beginAtZero:true,grid:{color:'#f1f5f9'},...(opts.x||{})},y:{grid:{display:false}}}}});
|
| 30 |
}
|
| 31 |
|
| 32 |
/* Tab System */
|
| 33 |
-
function initTabs(){
|
| 34 |
-
document.querySelectorAll('.tab-btn').forEach(btn=>{btn.addEventListener('click',()=>{document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));document.querySelectorAll('.tab-content').forEach(c=>c.classList.remove('active'));btn.classList.add('active');const p=document.getElementById(btn.dataset.tab);if(p){p.classList.add('active');if(!p.dataset.loaded){loadTab(btn.dataset.tab);p.dataset.loaded='1';}}});});
|
| 35 |
-
document.querySelectorAll('.sub-tab').forEach(btn=>{btn.addEventListener('click',()=>{const g=btn.closest('.sub-tabs'),ct=btn.closest('.tab-content')||document;g.querySelectorAll('.sub-tab').forEach(b=>b.classList.remove('active'));ct.querySelectorAll('.sub-panel').forEach(p=>p.classList.remove('active'));btn.classList.add('active');const p=ct.querySelector(`#${btn.dataset.subtab}`);if(p)p.classList.add('active');});});
|
| 36 |
}
|
| 37 |
|
| 38 |
-
function loadTab(id){
|
| 39 |
-
if(id==='tab-overview'){createFunnel();createDoughnut();createHeatmap();}
|
| 40 |
-
if(id==='tab-segments'){buildSegmentBars();buildMedMix();createPersonaFull('chart-pb-main',P.b,PL);createPersonaFull('chart-pc-main',P.c,PD);createPersonaFull('chart-pa-main',P.a,PB);}
|
| 41 |
-
if(id==='tab-adoption'){buildAdoptionPct();buildAdoptionAbs();buildGrowthSignals();buildTrendBars();}
|
| 42 |
-
if(id==='tab-competitive'){buildCompShare();buildCompRatio();buildScatterUC();}
|
| 43 |
-
if(id==='tab-engagement'){buildEngagement();buildScatterEng();}
|
| 44 |
-
if(id==='tab-opportunity'){buildOpportunityCharts();}
|
| 45 |
-
if(id==='tab-specialty'){buildSpecialtyStack();buildSpecialtyPct();}
|
| 46 |
|
| 47 |
}
|
| 48 |
|
| 49 |
/* Counters */
|
| 50 |
-
function animateCounters(){document.querySelectorAll('[data-count]').forEach(el=>{const t=parseFloat(el.dataset.count),sf=el.dataset.suffix||'',dur=1200,st=performance.now();(function u(now){const p=Math.min((now-st)/dur,1),v=t*(1-Math.pow(1-p,3));el.textContent=(el.dataset.count.includes('.')?v.toFixed(1):Math.round(v).toLocaleString())+sf;if(p<1)requestAnimationFrame(u);})(st);});}
|
| 51 |
|
| 52 |
/* ==================== TAB 1: OVERVIEW ==================== */
|
| 53 |
-
function createFunnel(){mkBar('chart-funnel',['Total Market','Labeled','Unlabeled','SEG_A','SEG_B','SEG_C'],[{data:[20931,11899,9032,6406,3349,2144],backgroundColor:['#e2e8f0','#cbd5e1','#94a3b8',PB,PL,PD],borderRadius:6,borderSkipped:false}],{plugins:{legend:{display:false}}});}
|
| 54 |
|
| 55 |
-
function createDoughnut(){const ctx=document.getElementById('chart-doughnut');if(!ctx)return;new Chart(ctx,{type:'doughnut',data:{labels:['SEG_A (Traditional)','SEG_B (Relationship)','SEG_C (Didactic)'],datasets:[{data:[6406,3349,2144],backgroundColor:[PB,PL,PD],borderColor:'#fff',borderWidth:4,hoverOffset:8}]},options:{maintainAspectRatio:false,cutout:'70%',responsive:true,plugins:{legend:{position:'bottom'},tooltip:{callbacks:{label:c=>`${c.label}: ${c.raw.toLocaleString()} HCPs (${(c.raw/11899*100).toFixed(1)}%)`}}}}});}
|
| 56 |
|
| 57 |
-
function createHeatmap(){
|
| 58 |
-
const feats=['UC TRx/wk','Pfizer TRx/wk','Pfizer Share','Trend Ratio','% Growing','Details/Rx','Biologic Loyalty','New Patient Orient.'];
|
| 59 |
-
const raw=[[0.1713,0.0005,0.0036,0.0769,0.0379,0.9443,0.0705,0.4367],[0.5174,0.0018,0.0048,0.2058,0.0964,0.4359,0.0984,0.4296],[0.7111,0.0017,0.0031,0.1957,0.0924,0.3843,0.1129,0.4294]];
|
| 60 |
-
|
| 61 |
-
const norm = [[],[],[]];
|
| 62 |
-
for(let f=0;f<8;f++){
|
| 63 |
const v = [raw[0][f], raw[1][f], raw[2][f]];
|
| 64 |
const minVal = Math.min(...v);
|
| 65 |
const range = Math.max(...v) - minVal || 1;
|
|
@@ -67,113 +67,195 @@ function createHeatmap(){
|
|
| 67 |
norm[1].push((raw[1][f] - minVal) / range);
|
| 68 |
norm[2].push((raw[2][f] - minVal) / range);
|
| 69 |
}
|
| 70 |
-
|
| 71 |
-
const ctx=document.getElementById('chart-heatmap');if(!ctx)return;
|
| 72 |
-
const datasets=SEGS.map((s,si)=>({label:s,data:norm[si],raw_data:raw[si],backgroundColor:SEG_COLORS[si],borderRadius:4,borderSkipped:false}));
|
| 73 |
-
new Chart(ctx,{type:'bar',data:{labels:feats,datasets},options:{maintainAspectRatio:false,responsive:true,plugins:{legend:{position:'bottom'},tooltip:{callbacks:{label:c=>c.dataset.label+': '+c.dataset.raw_data[c.dataIndex].toFixed(4)}}},scales:{y:{display:false,beginAtZero:true,max:1.1},x:{grid:{display:false},ticks:{font:{size:11},maxRotation:45}}}}});
|
| 74 |
}
|
| 75 |
|
| 76 |
/* ==================== TAB 2: SEGMENTS ==================== */
|
| 77 |
-
function buildSegmentBars(){mkBar('chart-segment-bars',SEGS,[{label:'UC TRx/week',data:[0.1713,0.5174,0.7111],backgroundColor:SEG_COLORS,borderRadius:6,borderSkipped:false}],{plugins:{legend:{display:false}}});}
|
| 78 |
-
function buildMedMix(){mkBar('chart-med-mix',SEGS,[{label:'Total UC TRx',data:[0.1713,0.5174,0.7111],backgroundColor:'#6B7A96',borderRadius:4},{label:'IL-23 Biologic',data:[0.0127,0.0597,0.0941],backgroundColor:CC,borderRadius:4},{label:'Oral TRx',data:[0.0234,0.1257,0.1400],backgroundColor:CB,borderRadius:4}]);}
|
| 79 |
|
| 80 |
-
function createPersonaFull(id,data,color){const ctx=document.getElementById(id);if(!ctx)return;new Chart(ctx,{type:'line',data:{labels:wk,datasets:[{label:'TRx Volume',data:data.trx,borderColor:color,backgroundColor:color+'10',fill:true,tension:0.4,borderWidth:3,pointRadius:0,pointHoverRadius:6,yAxisID:'y'},{label:'Engagement Score',data:data.eng,borderColor:AMBER,backgroundColor:'transparent',borderDash:[4,4],tension:0.4,borderWidth:2,pointRadius:0,pointHoverRadius:6,yAxisID:'y1'},{label:'New Rx (NRx)',data:data.nrx,borderColor:GREEN,backgroundColor:'transparent',tension:0.4,borderWidth:2,pointRadius:0,pointHoverRadius:6,yAxisID:'y1'}]},options:{responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},plugins:{legend:{position:'top'},tooltip:{mode:'index'}},scales:{y:{type:'linear',display:true,position:'left',beginAtZero:true,grid:{color:'#f1f5f9'},title:{display:true,text:'TRx / NRx Volume'}},y1:{type:'linear',display:true,position:'right',beginAtZero:true,grid:{drawOnChartArea:false},title:{display:true,text:'Marketing Interactions'}},x:{grid:{display:false},ticks:{maxTicksLimit:8}}}}});}
|
| 81 |
|
| 82 |
/* ==================== TAB 3: ADOPTION ==================== */
|
| 83 |
-
function buildAdoptionPct(){mkBar('chart-adoption-pct',SEGS,[{label:'Never Tried',data:[95.6,88.6,88.8],backgroundColor:RED,borderRadius:4},{label:'Active',data:[2.8,7.7,7.4],backgroundColor:GREEN,borderRadius:4},{label:'Lapsed',data:[1.6,3.7,3.8],backgroundColor:CC,borderRadius:4}],{extra:{plugins:{legend:{display:true,position:'bottom'}}},y:{stacked:true,max:105},x:{stacked:true}});}
|
| 84 |
-
function buildAdoptionAbs(){mkBar('chart-adoption-abs',SEGS,[{label:'Never Tried',data:[6124,2967,1903],backgroundColor:RED,borderRadius:4},{label:'Active',data:[181,257,159],backgroundColor:GREEN,borderRadius:4},{label:'Lapsed',data:[101,125,82],backgroundColor:CC,borderRadius:4}]);}
|
| 85 |
-
function buildGrowthSignals(){mkBar('chart-growth-signals',['B1 Growing (%)','New Adopter (%)','Active Last 8 Wks (%)'],[{label:'SEG_A',data:[3.79,3.72,2.83],backgroundColor:CA,borderRadius:4},{label:'SEG_B',data:[9.64,8.81,7.67],backgroundColor:CB,borderRadius:4},{label:'SEG_C',data:[9.24,8.44,7.42],backgroundColor:CC,borderRadius:4}]);}
|
| 86 |
-
function buildTrendBars(){mkBar('chart-trend-bars',['SEG_A (Avg)','SEG_A (Recent)','SEG_B (Avg)','SEG_B (Recent)','SEG_C (Avg)','SEG_C (Recent)'],[{data:[0.000504,0.001325,0.001835,0.004195,0.001720,0.004224],backgroundColor:[CA,CA,CB,CB,CC,CC].map((c,i)=>i%2===0?c+'80':c),borderRadius:6,borderSkipped:false}],{plugins:{legend:{display:false}}});}
|
| 87 |
|
| 88 |
/* ==================== TAB 4: COMPETITIVE ==================== */
|
| 89 |
-
function buildCompShare(){mkBar('chart-comp-share',SEGS,[{label:'Pfizer Share (%)',data:[0.363,0.480,0.311],backgroundColor:CB,borderRadius:4},{label:'Brand2 Share (%)',data:[1.429,2.153,1.250],backgroundColor:CC,borderRadius:4}]);}
|
| 90 |
-
function buildCompRatio(){mkBar('chart-comp-ratio',SEGS,[{data:[3.90,4.43,4.29],backgroundColor:SEG_COLORS,borderRadius:6,borderSkipped:false}],{plugins:{legend:{display:false}}});}
|
| 91 |
-
function buildScatterUC()
|
| 92 |
-
const
|
| 93 |
-
|
|
|
|
|
|
|
| 94 |
|
| 95 |
/* ==================== TAB 5: ENGAGEMENT ==================== */
|
| 96 |
-
function buildEngagement(){mkBar('chart-engagement',SEGS,[{label:'Details per Rx',data:[0.944,0.436,0.384],backgroundColor:SEG_COLORS,borderRadius:6,borderSkipped:false}],{plugins:{legend:{display:false}}});}
|
| 97 |
-
function buildScatterEng()
|
| 98 |
-
const
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
|
| 101 |
/* ==================== TAB 6: OPPORTUNITY ==================== */
|
| 102 |
-
function buildOpportunityCharts(){
|
| 103 |
-
fetch('opportunity_data.json').then(r=>r.json()).then(data=>{
|
| 104 |
// Add a tiny random jitter to the y-axis (Score) so overlapping points are visible
|
| 105 |
// We keep the original 'uc' and 'sc' to show in tooltips
|
| 106 |
const jitter = () => (Math.random() - 0.5) * 0.04;
|
| 107 |
-
const nv=data.noVisits.map(h=>({x:h.uc, y:Math.max(0, h.sc + jitter()), ...h}));
|
| 108 |
-
const cv=data.covered.map(h=>({x:h.uc, y:Math.max(0, h.sc + jitter()), ...h}));
|
| 109 |
-
|
| 110 |
// Histogram
|
| 111 |
const all = [...data.noVisits, ...data.covered];
|
| 112 |
-
const scores = all.map(h=>h.sc);
|
| 113 |
const minSc = Math.min(...scores);
|
| 114 |
const maxSc = Math.max(...scores);
|
| 115 |
-
|
| 116 |
const numBins = 20;
|
| 117 |
const binWidth = (maxSc > minSc) ? (maxSc - minSc) / numBins : 1;
|
| 118 |
let edges = [];
|
| 119 |
-
for(let i=0; i<=numBins; i++) edges.push(minSc + i * binWidth);
|
| 120 |
-
|
| 121 |
let bins = Array(numBins).fill(0);
|
| 122 |
scores.forEach(s => {
|
| 123 |
let b = Math.floor((s - minSc) / binWidth);
|
| 124 |
if (b >= numBins) b = numBins - 1;
|
| 125 |
bins[b]++;
|
| 126 |
});
|
| 127 |
-
|
| 128 |
-
const histLabels = edges.slice(0, -1).map((e, i) => ((e + edges[i+1])/2).toFixed(2));
|
| 129 |
-
mkBar('chart-opp-hist', histLabels, [{data:bins, backgroundColor:CU+'cc', borderRadius:2, borderSkipped:false}], {plugins:{legend:{display:false}},x:{ticks:{maxTicksLimit:10,font:{size:10}}}});
|
| 130 |
|
| 131 |
// Scatter Plot
|
| 132 |
-
const ctx=document.getElementById('chart-opp-scatter');if(!ctx)return;
|
| 133 |
-
const chart=new Chart(ctx,{
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
}
|
| 158 |
-
}
|
| 159 |
});
|
| 160 |
}
|
| 161 |
|
| 162 |
/* ==================== TAB 7: SPECIALTY ==================== */
|
| 163 |
-
function buildSpecialtyStack(){
|
| 164 |
-
const sp=['GP/Family Med','Gastroenterology','Internal Med','Neuro/Rheum','Other Spec','Pharmacy'];
|
| 165 |
-
mkHBar('chart-spec-stack',sp,[{label:'SEG_A',data:[25,6256,74,13,29,9],backgroundColor:CA},{label:'SEG_B',data:[8,3297,13,5,23,3],backgroundColor:CB},{label:'SEG_C',data:[2,2127,3,3,8,1],backgroundColor:CC}],{x:{stacked:true}});
|
| 166 |
}
|
| 167 |
-
function buildSpecialtyPct(){
|
| 168 |
-
const sp=['GP/Family Med','Gastroenterology','Internal Med','Neuro/Rheum','Other Spec','Pharmacy'];
|
| 169 |
-
const sa=[25,6256,74,13,29,9],sb=[8,3297,13,5,23,3],sc=[2,2127,3,3,8,1];
|
| 170 |
-
const pctA=sa.map((_,i)=>{const t=sa[i]+sb[i]+sc[i];return t?+(sa[i]/t*100).toFixed(1):0;});
|
| 171 |
-
const pctB=sb.map((_,i)=>{const t=sa[i]+sb[i]+sc[i];return t?+(sb[i]/t*100).toFixed(1):0;});
|
| 172 |
-
const pctC=sc.map((_,i)=>{const t=sa[i]+sb[i]+sc[i];return t?+(sc[i]/t*100).toFixed(1):0;});
|
| 173 |
-
mkHBar('chart-spec-pct',sp,[{label:'SEG_A %',data:pctA,backgroundColor:CA},{label:'SEG_B %',data:pctB,backgroundColor:CB},{label:'SEG_C %',data:pctC,backgroundColor:CC}],{x:{stacked:true,max:100}});
|
| 174 |
}
|
| 175 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
/* Init */
|
| 179 |
-
document.addEventListener('DOMContentLoaded',()=>{initTabs();loadTab('tab-overview');animateCounters();});
|
|
|
|
| 2 |
HCP Segmentation Dashboard β Chart.js Unified Engine
|
| 3 |
All data from hcp_analysis_clean.parquet (191 columns)
|
| 4 |
============================================================ */
|
| 5 |
+
Chart.defaults.color = '#64748b'; Chart.defaults.borderColor = '#e2e8f0'; Chart.defaults.font.family = "'Inter',sans-serif"; Chart.defaults.font.size = 13;
|
| 6 |
+
Chart.defaults.plugins.legend.labels.usePointStyle = true; Chart.defaults.plugins.legend.labels.pointStyle = 'circle'; Chart.defaults.plugins.legend.labels.padding = 20;
|
| 7 |
+
Chart.defaults.plugins.tooltip.backgroundColor = '#1e293b'; Chart.defaults.plugins.tooltip.padding = 14; Chart.defaults.plugins.tooltip.cornerRadius = 8;
|
| 8 |
|
| 9 |
+
const PB = '#0051a5', PL = '#00a3e0', PD = '#0d009d', PS = '#54c8e8';
|
| 10 |
+
const CA = '#6B7280', CB = '#1A6FD4', CC = '#D4720A', CU = '#7C3AED';
|
| 11 |
+
const GREEN = '#0D9E6E', RED = '#DC3545', AMBER = '#d97706';
|
| 12 |
+
const SEGS = ['SEG_A', 'SEG_B', 'SEG_C'];
|
| 13 |
+
const SEG_COLORS = [CA, CB, CC];
|
| 14 |
|
| 15 |
/* Simulated weekly persona data (illustrative timeline shapes) */
|
| 16 |
+
function gen(base, trend, noise, n) { const d = []; for (let i = 0; i < n; i++)d.push(Math.max(0, Math.round((base + trend * i + (Math.random() - 0.5) * noise) * 10) / 10)); return d; }
|
| 17 |
+
const W = 20, wk = Array.from({ length: W }, (_, i) => `W${(i + 1) * 4}`);
|
| 18 |
+
const P = { a: { trx: gen(12, 0, 1.5, W), eng: gen(2, 0.02, 0.5, W), nrx: gen(1, 0, 0.5, W) }, b: { trx: gen(8, 0.4, 2, W), eng: gen(5, 0.3, 1, W), nrx: gen(3, 0.25, 0.8, W) }, c: { trx: gen(5, 0.15, 1.5, W), eng: gen(3, 0.1, 1.2, W), nrx: gen(2, 0.08, 0.6, W) } };
|
| 19 |
|
| 20 |
/* Helper: create a bar chart */
|
| 21 |
+
function mkBar(id, labels, datasets, opts = {}) {
|
| 22 |
+
const ctx = document.getElementById(id); if (!ctx) return null;
|
| 23 |
+
return new Chart(ctx, { type: 'bar', data: { labels, datasets }, options: { maintainAspectRatio: false, responsive: true, plugins: { legend: { display: datasets.length > 1, position: 'bottom' }, ...(opts.plugins || {}) }, scales: { y: { beginAtZero: true, grid: { color: '#f1f5f9' }, ...(opts.y || {}) }, x: { grid: { display: false }, ...(opts.x || {}) } }, ...(opts.extra || {}) } });
|
| 24 |
}
|
| 25 |
|
| 26 |
/* Helper: horizontal bar */
|
| 27 |
+
function mkHBar(id, labels, datasets, opts = {}) {
|
| 28 |
+
const ctx = document.getElementById(id); if (!ctx) return null;
|
| 29 |
+
return new Chart(ctx, { type: 'bar', data: { labels, datasets }, options: { maintainAspectRatio: false, responsive: true, indexAxis: 'y', plugins: { legend: { display: datasets.length > 1, position: 'bottom' } }, scales: { x: { beginAtZero: true, grid: { color: '#f1f5f9' }, ...(opts.x || {}) }, y: { grid: { display: false } } } } });
|
| 30 |
}
|
| 31 |
|
| 32 |
/* Tab System */
|
| 33 |
+
function initTabs() {
|
| 34 |
+
document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); btn.classList.add('active'); const p = document.getElementById(btn.dataset.tab); if (p) { p.classList.add('active'); if (!p.dataset.loaded) { loadTab(btn.dataset.tab); p.dataset.loaded = '1'; } } }); });
|
| 35 |
+
document.querySelectorAll('.sub-tab').forEach(btn => { btn.addEventListener('click', () => { const g = btn.closest('.sub-tabs'), ct = btn.closest('.tab-content') || document; g.querySelectorAll('.sub-tab').forEach(b => b.classList.remove('active')); ct.querySelectorAll('.sub-panel').forEach(p => p.classList.remove('active')); btn.classList.add('active'); const p = ct.querySelector(`#${btn.dataset.subtab}`); if (p) p.classList.add('active'); }); });
|
| 36 |
}
|
| 37 |
|
| 38 |
+
function loadTab(id) {
|
| 39 |
+
if (id === 'tab-overview') { createFunnel(); createDoughnut(); createHeatmap(); }
|
| 40 |
+
if (id === 'tab-segments') { buildSegmentBars(); buildMedMix(); createPersonaFull('chart-pb-main', P.b, PL); createPersonaFull('chart-pc-main', P.c, PD); createPersonaFull('chart-pa-main', P.a, PB); }
|
| 41 |
+
if (id === 'tab-adoption') { buildAdoptionPct(); buildAdoptionAbs(); buildGrowthSignals(); buildTrendBars(); }
|
| 42 |
+
if (id === 'tab-competitive') { buildCompShare(); buildCompRatio(); buildScatterUC(); }
|
| 43 |
+
if (id === 'tab-engagement') { buildEngagement(); buildScatterEng(); }
|
| 44 |
+
if (id === 'tab-opportunity') { buildOpportunityCharts(); }
|
| 45 |
+
if (id === 'tab-specialty') { buildSpecialtyStack(); buildSpecialtyPct(); }
|
| 46 |
|
| 47 |
}
|
| 48 |
|
| 49 |
/* Counters */
|
| 50 |
+
function animateCounters() { document.querySelectorAll('[data-count]').forEach(el => { const t = parseFloat(el.dataset.count), sf = el.dataset.suffix || '', dur = 1200, st = performance.now(); (function u(now) { const p = Math.min((now - st) / dur, 1), v = t * (1 - Math.pow(1 - p, 3)); el.textContent = (el.dataset.count.includes('.') ? v.toFixed(1) : Math.round(v).toLocaleString()) + sf; if (p < 1) requestAnimationFrame(u); })(st); }); }
|
| 51 |
|
| 52 |
/* ==================== TAB 1: OVERVIEW ==================== */
|
| 53 |
+
function createFunnel() { mkBar('chart-funnel', ['Total Market', 'Labeled', 'Unlabeled', 'SEG_A', 'SEG_B', 'SEG_C'], [{ data: [20931, 11899, 9032, 6406, 3349, 2144], backgroundColor: ['#e2e8f0', '#cbd5e1', '#94a3b8', PB, PL, PD], borderRadius: 6, borderSkipped: false }], { plugins: { legend: { display: false } } }); }
|
| 54 |
|
| 55 |
+
function createDoughnut() { const ctx = document.getElementById('chart-doughnut'); if (!ctx) return; new Chart(ctx, { type: 'doughnut', data: { labels: ['SEG_A (Traditional)', 'SEG_B (Relationship)', 'SEG_C (Didactic)'], datasets: [{ data: [6406, 3349, 2144], backgroundColor: [PB, PL, PD], borderColor: '#fff', borderWidth: 4, hoverOffset: 8 }] }, options: { maintainAspectRatio: false, cutout: '70%', responsive: true, plugins: { legend: { position: 'bottom' }, tooltip: { callbacks: { label: c => `${c.label}: ${c.raw.toLocaleString()} HCPs (${(c.raw / 11899 * 100).toFixed(1)}%)` } } } } }); }
|
| 56 |
|
| 57 |
+
function createHeatmap() {
|
| 58 |
+
const feats = ['UC TRx/wk', 'Pfizer TRx/wk', 'Pfizer Share', 'Trend Ratio', '% Growing', 'Details/Rx', 'Biologic Loyalty', 'New Patient Orient.'];
|
| 59 |
+
const raw = [[0.1713, 0.0005, 0.0036, 0.0769, 0.0379, 0.9443, 0.0705, 0.4367], [0.5174, 0.0018, 0.0048, 0.2058, 0.0964, 0.4359, 0.0984, 0.4296], [0.7111, 0.0017, 0.0031, 0.1957, 0.0924, 0.3843, 0.1129, 0.4294]];
|
| 60 |
+
|
| 61 |
+
const norm = [[], [], []];
|
| 62 |
+
for (let f = 0; f < 8; f++) {
|
| 63 |
const v = [raw[0][f], raw[1][f], raw[2][f]];
|
| 64 |
const minVal = Math.min(...v);
|
| 65 |
const range = Math.max(...v) - minVal || 1;
|
|
|
|
| 67 |
norm[1].push((raw[1][f] - minVal) / range);
|
| 68 |
norm[2].push((raw[2][f] - minVal) / range);
|
| 69 |
}
|
| 70 |
+
|
| 71 |
+
const ctx = document.getElementById('chart-heatmap'); if (!ctx) return;
|
| 72 |
+
const datasets = SEGS.map((s, si) => ({ label: s, data: norm[si], raw_data: raw[si], backgroundColor: SEG_COLORS[si], borderRadius: 4, borderSkipped: false }));
|
| 73 |
+
new Chart(ctx, { type: 'bar', data: { labels: feats, datasets }, options: { maintainAspectRatio: false, responsive: true, plugins: { legend: { position: 'bottom' }, tooltip: { callbacks: { label: c => c.dataset.label + ': ' + c.dataset.raw_data[c.dataIndex].toFixed(4) } } }, scales: { y: { display: false, beginAtZero: true, max: 1.1 }, x: { grid: { display: false }, ticks: { font: { size: 11 }, maxRotation: 45 } } } } });
|
| 74 |
}
|
| 75 |
|
| 76 |
/* ==================== TAB 2: SEGMENTS ==================== */
|
| 77 |
+
function buildSegmentBars() { mkBar('chart-segment-bars', SEGS, [{ label: 'UC TRx/week', data: [0.1713, 0.5174, 0.7111], backgroundColor: SEG_COLORS, borderRadius: 6, borderSkipped: false }], { plugins: { legend: { display: false } } }); }
|
| 78 |
+
function buildMedMix() { mkBar('chart-med-mix', SEGS, [{ label: 'Total UC TRx', data: [0.1713, 0.5174, 0.7111], backgroundColor: '#6B7A96', borderRadius: 4 }, { label: 'IL-23 Biologic', data: [0.0127, 0.0597, 0.0941], backgroundColor: CC, borderRadius: 4 }, { label: 'Oral TRx', data: [0.0234, 0.1257, 0.1400], backgroundColor: CB, borderRadius: 4 }]); }
|
| 79 |
|
| 80 |
+
function createPersonaFull(id, data, color) { const ctx = document.getElementById(id); if (!ctx) return; new Chart(ctx, { type: 'line', data: { labels: wk, datasets: [{ label: 'TRx Volume', data: data.trx, borderColor: color, backgroundColor: color + '10', fill: true, tension: 0.4, borderWidth: 3, pointRadius: 0, pointHoverRadius: 6, yAxisID: 'y' }, { label: 'Engagement Score', data: data.eng, borderColor: AMBER, backgroundColor: 'transparent', borderDash: [4, 4], tension: 0.4, borderWidth: 2, pointRadius: 0, pointHoverRadius: 6, yAxisID: 'y1' }, { label: 'New Rx (NRx)', data: data.nrx, borderColor: GREEN, backgroundColor: 'transparent', tension: 0.4, borderWidth: 2, pointRadius: 0, pointHoverRadius: 6, yAxisID: 'y1' }] }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, plugins: { legend: { position: 'top' }, tooltip: { mode: 'index' } }, scales: { y: { type: 'linear', display: true, position: 'left', beginAtZero: true, grid: { color: '#f1f5f9' }, title: { display: true, text: 'TRx / NRx Volume' } }, y1: { type: 'linear', display: true, position: 'right', beginAtZero: true, grid: { drawOnChartArea: false }, title: { display: true, text: 'Marketing Interactions' } }, x: { grid: { display: false }, ticks: { maxTicksLimit: 8 } } } } }); }
|
| 81 |
|
| 82 |
/* ==================== TAB 3: ADOPTION ==================== */
|
| 83 |
+
function buildAdoptionPct() { mkBar('chart-adoption-pct', SEGS, [{ label: 'Never Tried', data: [95.6, 88.6, 88.8], backgroundColor: RED, borderRadius: 4 }, { label: 'Active', data: [2.8, 7.7, 7.4], backgroundColor: GREEN, borderRadius: 4 }, { label: 'Lapsed', data: [1.6, 3.7, 3.8], backgroundColor: CC, borderRadius: 4 }], { extra: { plugins: { legend: { display: true, position: 'bottom' } } }, y: { stacked: true, max: 105 }, x: { stacked: true } }); }
|
| 84 |
+
function buildAdoptionAbs() { mkBar('chart-adoption-abs', SEGS, [{ label: 'Never Tried', data: [6124, 2967, 1903], backgroundColor: RED, borderRadius: 4 }, { label: 'Active', data: [181, 257, 159], backgroundColor: GREEN, borderRadius: 4 }, { label: 'Lapsed', data: [101, 125, 82], backgroundColor: CC, borderRadius: 4 }]); }
|
| 85 |
+
function buildGrowthSignals() { mkBar('chart-growth-signals', ['B1 Growing (%)', 'New Adopter (%)', 'Active Last 8 Wks (%)'], [{ label: 'SEG_A', data: [3.79, 3.72, 2.83], backgroundColor: CA, borderRadius: 4 }, { label: 'SEG_B', data: [9.64, 8.81, 7.67], backgroundColor: CB, borderRadius: 4 }, { label: 'SEG_C', data: [9.24, 8.44, 7.42], backgroundColor: CC, borderRadius: 4 }]); }
|
| 86 |
+
function buildTrendBars() { mkBar('chart-trend-bars', ['SEG_A (Avg)', 'SEG_A (Recent)', 'SEG_B (Avg)', 'SEG_B (Recent)', 'SEG_C (Avg)', 'SEG_C (Recent)'], [{ data: [0.000504, 0.001325, 0.001835, 0.004195, 0.001720, 0.004224], backgroundColor: [CA, CA, CB, CB, CC, CC].map((c, i) => i % 2 === 0 ? c + '80' : c), borderRadius: 6, borderSkipped: false }], { plugins: { legend: { display: false } } }); }
|
| 87 |
|
| 88 |
/* ==================== TAB 4: COMPETITIVE ==================== */
|
| 89 |
+
function buildCompShare() { mkBar('chart-comp-share', SEGS, [{ label: 'Pfizer Share (%)', data: [0.363, 0.480, 0.311], backgroundColor: CB, borderRadius: 4 }, { label: 'Brand2 Share (%)', data: [1.429, 2.153, 1.250], backgroundColor: CC, borderRadius: 4 }]); }
|
| 90 |
+
function buildCompRatio() { mkBar('chart-comp-ratio', SEGS, [{ data: [3.90, 4.43, 4.29], backgroundColor: SEG_COLORS, borderRadius: 6, borderSkipped: false }], { plugins: { legend: { display: false } } }); }
|
| 91 |
+
function buildScatterUC() {
|
| 92 |
+
const ctx = document.getElementById('chart-scatter-uc'); if (!ctx) return;
|
| 93 |
+
const mk = (n, ub, sb) => { const d = []; for (let i = 0; i < n; i++)d.push({ x: Math.max(0, ub + Math.random() * ub * 3), y: Math.max(0, Math.min(0.15, sb + Math.random() * sb * 4 - sb * 1.5)) }); return d; };
|
| 94 |
+
new Chart(ctx, { type: 'scatter', data: { datasets: [{ label: 'SEG_A', data: mk(400, 0.17, 0.004), backgroundColor: CA + '66', pointRadius: 3 }, { label: 'SEG_B', data: mk(300, 0.52, 0.005), backgroundColor: CB + '66', pointRadius: 3 }, { label: 'SEG_C', data: mk(200, 0.71, 0.003), backgroundColor: CC + '66', pointRadius: 3 }] }, options: { maintainAspectRatio: false, responsive: true, plugins: { legend: { position: 'bottom' } }, scales: { x: { title: { display: true, text: 'UC TRx Mean (weekly)' }, grid: { color: '#f1f5f9' } }, y: { title: { display: true, text: 'Pfizer Share of UC' }, grid: { color: '#f1f5f9' } } } } });
|
| 95 |
+
}
|
| 96 |
|
| 97 |
/* ==================== TAB 5: ENGAGEMENT ==================== */
|
| 98 |
+
function buildEngagement() { mkBar('chart-engagement', SEGS, [{ label: 'Details per Rx', data: [0.944, 0.436, 0.384], backgroundColor: SEG_COLORS, borderRadius: 6, borderSkipped: false }], { plugins: { legend: { display: false } } }); }
|
| 99 |
+
function buildScatterEng() {
|
| 100 |
+
const ctx = document.getElementById('chart-scatter-eng'); if (!ctx) return;
|
| 101 |
+
const mk = (n, db, bb) => { const d = []; for (let i = 0; i < n; i++)d.push({ x: Math.max(0, db + Math.random() * db * 3), y: Math.max(0, bb + Math.random() * bb * 4 - bb) }); return d; };
|
| 102 |
+
new Chart(ctx, { type: 'scatter', data: { datasets: [{ label: 'SEG_A', data: mk(400, 5.28, 0.0005), backgroundColor: CA + '66', pointRadius: 3 }, { label: 'SEG_B', data: mk(300, 8.94, 0.0018), backgroundColor: CB + '66', pointRadius: 3 }, { label: 'SEG_C', data: mk(200, 8.71, 0.0017), backgroundColor: CC + '66', pointRadius: 3 }] }, options: { maintainAspectRatio: false, responsive: true, plugins: { legend: { position: 'bottom' } }, scales: { x: { title: { display: true, text: 'Total Rep Visits (86 wks)' }, grid: { color: '#f1f5f9' } }, y: { title: { display: true, text: 'Pfizer TRx / week' }, grid: { color: '#f1f5f9' } } } } });
|
| 103 |
+
}
|
| 104 |
|
| 105 |
/* ==================== TAB 6: OPPORTUNITY ==================== */
|
| 106 |
+
function buildOpportunityCharts() {
|
| 107 |
+
fetch('opportunity_data.json').then(r => r.json()).then(data => {
|
| 108 |
// Add a tiny random jitter to the y-axis (Score) so overlapping points are visible
|
| 109 |
// We keep the original 'uc' and 'sc' to show in tooltips
|
| 110 |
const jitter = () => (Math.random() - 0.5) * 0.04;
|
| 111 |
+
const nv = data.noVisits.map(h => ({ x: h.uc, y: Math.max(0, h.sc + jitter()), ...h }));
|
| 112 |
+
const cv = data.covered.map(h => ({ x: h.uc, y: Math.max(0, h.sc + jitter()), ...h }));
|
| 113 |
+
|
| 114 |
// Histogram
|
| 115 |
const all = [...data.noVisits, ...data.covered];
|
| 116 |
+
const scores = all.map(h => h.sc);
|
| 117 |
const minSc = Math.min(...scores);
|
| 118 |
const maxSc = Math.max(...scores);
|
| 119 |
+
|
| 120 |
const numBins = 20;
|
| 121 |
const binWidth = (maxSc > minSc) ? (maxSc - minSc) / numBins : 1;
|
| 122 |
let edges = [];
|
| 123 |
+
for (let i = 0; i <= numBins; i++) edges.push(minSc + i * binWidth);
|
| 124 |
+
|
| 125 |
let bins = Array(numBins).fill(0);
|
| 126 |
scores.forEach(s => {
|
| 127 |
let b = Math.floor((s - minSc) / binWidth);
|
| 128 |
if (b >= numBins) b = numBins - 1;
|
| 129 |
bins[b]++;
|
| 130 |
});
|
| 131 |
+
|
| 132 |
+
const histLabels = edges.slice(0, -1).map((e, i) => ((e + edges[i + 1]) / 2).toFixed(2));
|
| 133 |
+
mkBar('chart-opp-hist', histLabels, [{ data: bins, backgroundColor: CU + 'cc', borderRadius: 2, borderSkipped: false }], { plugins: { legend: { display: false } }, x: { ticks: { maxTicksLimit: 10, font: { size: 10 } } } });
|
| 134 |
|
| 135 |
// Scatter Plot
|
| 136 |
+
const ctx = document.getElementById('chart-opp-scatter'); if (!ctx) return;
|
| 137 |
+
const chart = new Chart(ctx, {
|
| 138 |
+
type: 'scatter', data: {
|
| 139 |
+
datasets: [
|
| 140 |
+
{ label: 'No Rep Visits', data: nv, backgroundColor: RED + 'aa', pointRadius: 4, pointStyle: 'circle' },
|
| 141 |
+
{ label: 'Covered', data: cv, backgroundColor: CB + '88', pointRadius: 4, pointStyle: 'rect' }
|
| 142 |
+
]
|
| 143 |
+
}, options: {
|
| 144 |
+
maintainAspectRatio: false, responsive: true,
|
| 145 |
+
plugins: {
|
| 146 |
+
legend: { position: 'bottom' }, tooltip: {
|
| 147 |
+
callbacks: {
|
| 148 |
+
title: pts => { const p = pts[0]; return p.datasetIndex === 0 ? 'ID: ' + p.raw.id : 'Covered HCP'; },
|
| 149 |
+
label: p => [`UC TRx: ${p.raw.uc.toFixed(4)}/wk`, `Score: ${p.raw.sc.toFixed(4)}`, p.raw.sp ? `Specialty: ${p.raw.sp}` : '']
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
},
|
| 153 |
+
scales: { x: { title: { display: true, text: 'UC TRx Mean (weekly)' }, grid: { color: '#f1f5f9' } }, y: { title: { display: true, text: 'Opportunity Score' }, grid: { color: '#f1f5f9' } } },
|
| 154 |
+
onClick: (evt, els) => {
|
| 155 |
+
if (!els.length) return;
|
| 156 |
+
const el = els[0], di = el.datasetIndex, idx = el.index;
|
| 157 |
+
if (di !== 0) return;
|
| 158 |
+
const hcp = chart.data.datasets[0].data[idx];
|
| 159 |
+
const panel = document.getElementById('hcp-detail-panel');
|
| 160 |
+
document.getElementById('hcp-detail-title').textContent = 'NUEVO_ID: ' + hcp.id;
|
| 161 |
+
document.getElementById('hcp-detail-grid').innerHTML =
|
| 162 |
+
`<div class="card kpi-card"><div class="kpi-label">HCP ID</div><div class="kpi-value" style="font-size:22px;color:${RED}">${hcp.id}</div></div>` +
|
| 163 |
+
`<div class="card kpi-card"><div class="kpi-label">Specialty</div><div class="kpi-value" style="font-size:16px">${hcp.sp}</div></div>` +
|
| 164 |
+
`<div class="card kpi-card"><div class="kpi-label">UC TRx / Week</div><div class="kpi-value" style="font-size:22px">${hcp.uc.toFixed(4)}</div></div>` +
|
| 165 |
+
`<div class="card kpi-card"><div class="kpi-label">Opportunity Score</div><div class="kpi-value" style="font-size:22px;color:${CU}">${hcp.sc.toFixed(4)}</div></div>` +
|
| 166 |
+
`<div class="card kpi-card"><div class="kpi-label">Active Weeks</div><div class="kpi-value" style="font-size:22px">${hcp.ap}%</div></div>`;
|
| 167 |
+
panel.style.display = 'block';
|
| 168 |
+
panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
| 169 |
+
}
|
| 170 |
}
|
| 171 |
+
});
|
| 172 |
});
|
| 173 |
}
|
| 174 |
|
| 175 |
/* ==================== TAB 7: SPECIALTY ==================== */
|
| 176 |
+
function buildSpecialtyStack() {
|
| 177 |
+
const sp = ['GP/Family Med', 'Gastroenterology', 'Internal Med', 'Neuro/Rheum', 'Other Spec', 'Pharmacy'];
|
| 178 |
+
mkHBar('chart-spec-stack', sp, [{ label: 'SEG_A', data: [25, 6256, 74, 13, 29, 9], backgroundColor: CA }, { label: 'SEG_B', data: [8, 3297, 13, 5, 23, 3], backgroundColor: CB }, { label: 'SEG_C', data: [2, 2127, 3, 3, 8, 1], backgroundColor: CC }], { x: { stacked: true } });
|
| 179 |
}
|
| 180 |
+
function buildSpecialtyPct() {
|
| 181 |
+
const sp = ['GP/Family Med', 'Gastroenterology', 'Internal Med', 'Neuro/Rheum', 'Other Spec', 'Pharmacy'];
|
| 182 |
+
const sa = [25, 6256, 74, 13, 29, 9], sb = [8, 3297, 13, 5, 23, 3], sc = [2, 2127, 3, 3, 8, 1];
|
| 183 |
+
const pctA = sa.map((_, i) => { const t = sa[i] + sb[i] + sc[i]; return t ? +(sa[i] / t * 100).toFixed(1) : 0; });
|
| 184 |
+
const pctB = sb.map((_, i) => { const t = sa[i] + sb[i] + sc[i]; return t ? +(sb[i] / t * 100).toFixed(1) : 0; });
|
| 185 |
+
const pctC = sc.map((_, i) => { const t = sa[i] + sb[i] + sc[i]; return t ? +(sc[i] / t * 100).toFixed(1) : 0; });
|
| 186 |
+
mkHBar('chart-spec-pct', sp, [{ label: 'SEG_A %', data: pctA, backgroundColor: CA }, { label: 'SEG_B %', data: pctB, backgroundColor: CB }, { label: 'SEG_C %', data: pctC, backgroundColor: CC }], { x: { stacked: true, max: 100 } });
|
| 187 |
}
|
| 188 |
|
| 189 |
+
/* ============================================================
|
| 190 |
+
HUGGING FACE INFERENCE API INTEGRATION
|
| 191 |
+
Model: pfizer-project-team/binary-segA-vs-segBC
|
| 192 |
+
============================================================ */
|
| 193 |
+
|
| 194 |
+
const HF_API_URL = "https://api-inference.huggingface.co/models/pfizer-project-team/binary-segA-vs-segBC";
|
| 195 |
+
// Note: If your model is private, you need a token. If it's public, you might not need it,
|
| 196 |
+
// but it is highly recommended to avoid rate limits.
|
| 197 |
+
const HF_TOKEN = "YOUR_HF_ACCESS_TOKEN";
|
| 198 |
+
|
| 199 |
+
async function runModelPrediction() {
|
| 200 |
+
const resultDiv = document.getElementById('prediction-result');
|
| 201 |
+
const predictBtn = document.getElementById('btn-predict');
|
| 202 |
|
| 203 |
+
// UI Loading state
|
| 204 |
+
resultDiv.style.display = 'block';
|
| 205 |
+
resultDiv.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Running inference on Hugging Face...';
|
| 206 |
+
predictBtn.disabled = true;
|
| 207 |
+
predictBtn.style.opacity = '0.5';
|
| 208 |
+
|
| 209 |
+
// Construct the payload.
|
| 210 |
+
// IMPORTANT: Modify the keys and array lengths to match the exact input features your model expects.
|
| 211 |
+
const payload = {
|
| 212 |
+
"inputs": {
|
| 213 |
+
"DETAILS__std": [2.5],
|
| 214 |
+
"COPAY__mean": [15.0],
|
| 215 |
+
"ORAL_NRX__slope": [0.001],
|
| 216 |
+
"UC_TRX__max": [12.0]
|
| 217 |
+
}
|
| 218 |
+
};
|
| 219 |
+
|
| 220 |
+
try {
|
| 221 |
+
const response = await fetch(HF_API_URL, {
|
| 222 |
+
method: "POST",
|
| 223 |
+
headers: {
|
| 224 |
+
"Authorization": `Bearer ${HF_TOKEN}`,
|
| 225 |
+
"Content-Type": "application/json"
|
| 226 |
+
},
|
| 227 |
+
body: JSON.stringify(payload)
|
| 228 |
+
});
|
| 229 |
+
|
| 230 |
+
if (!response.ok) {
|
| 231 |
+
throw new Error(`HTTP Status: ${response.status}`);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
const data = await response.json();
|
| 235 |
+
|
| 236 |
+
// Format the output dynamically
|
| 237 |
+
// Scikit-learn / XGBoost models typically return an array of predictions
|
| 238 |
+
const predictionValue = Array.isArray(data) ? data[0] : JSON.stringify(data);
|
| 239 |
+
|
| 240 |
+
resultDiv.innerHTML = `<i class="fas fa-check-circle" style="color: var(--accent-green);"></i> Model Output: <b>${predictionValue}</b>`;
|
| 241 |
+
|
| 242 |
+
} catch (error) {
|
| 243 |
+
console.error("HF Inference API Error:", error);
|
| 244 |
+
resultDiv.innerHTML = `<i class="fas fa-exclamation-triangle" style="color: var(--accent-coral);"></i> Error running model. Please check the console.`;
|
| 245 |
+
} finally {
|
| 246 |
+
// Reset button state
|
| 247 |
+
predictBtn.disabled = false;
|
| 248 |
+
predictBtn.style.opacity = '1';
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
// Attach the event listener once the DOM is fully loaded
|
| 253 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 254 |
+
const predictBtn = document.getElementById('btn-predict');
|
| 255 |
+
if (predictBtn) {
|
| 256 |
+
predictBtn.addEventListener('click', runModelPrediction);
|
| 257 |
+
}
|
| 258 |
+
});
|
| 259 |
|
| 260 |
/* Init */
|
| 261 |
+
document.addEventListener('DOMContentLoaded', () => { initTabs(); loadTab('tab-overview'); animateCounters(); });
|
index.html
CHANGED
|
@@ -1,244 +1,531 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
|
|
|
| 3 |
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 5 |
-
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
| 6 |
-
<title>HCP Segmentation Dashboard β Pfizer UC</title>
|
| 7 |
-
<meta name="description"
|
| 8 |
-
|
| 9 |
-
<link rel="stylesheet" href="
|
| 10 |
-
|
| 11 |
-
|
|
|
|
| 12 |
</head>
|
|
|
|
| 13 |
<body>
|
| 14 |
|
| 15 |
-
<!-- Top Header -->
|
| 16 |
-
<header class="top-header">
|
| 17 |
-
<div class="top-header-left">
|
| 18 |
-
<img src="Pfizer_Logo_Color_RGB.png" alt="Pfizer">
|
| 19 |
-
<h1>HCP Segmentation Insights</h1>
|
| 20 |
-
<span>Ulcerative Colitis β 191-Feature Deep Analytics</span>
|
| 21 |
-
</div>
|
| 22 |
-
<div><span class="header-badge">Pipeline Stable</span></div>
|
| 23 |
-
</header>
|
| 24 |
-
|
| 25 |
-
<!-- Tab Navigation -->
|
| 26 |
-
<nav class="tab-nav">
|
| 27 |
-
<button class="tab-btn active" data-tab="tab-overview"><i class="fas fa-chart-pie"></i> Executive
|
| 28 |
-
<
|
| 29 |
-
<button class="tab-btn" data-tab="tab-
|
| 30 |
-
<button class="tab-btn" data-tab="tab-
|
| 31 |
-
<button class="tab-btn" data-tab="tab-
|
| 32 |
-
<button class="tab-btn" data-tab="tab-
|
| 33 |
-
<button class="tab-btn" data-tab="tab-
|
| 34 |
-
|
| 35 |
-
</
|
| 36 |
-
|
| 37 |
-
<
|
| 38 |
-
|
| 39 |
-
<
|
| 40 |
-
|
| 41 |
-
<div class="
|
| 42 |
-
<div class="
|
| 43 |
-
<div
|
| 44 |
-
<div class="
|
| 45 |
-
<div class="
|
| 46 |
-
<
|
| 47 |
-
</div>
|
| 48 |
-
|
| 49 |
-
<div class="
|
| 50 |
-
|
| 51 |
-
<div class="
|
| 52 |
-
<div class="
|
| 53 |
-
<
|
| 54 |
-
</div>
|
| 55 |
-
<div class="
|
| 56 |
-
</div>
|
| 57 |
-
|
| 58 |
-
<
|
| 59 |
-
<div
|
| 60 |
-
<
|
| 61 |
-
|
| 62 |
-
<div class="
|
| 63 |
-
<
|
| 64 |
-
<div
|
| 65 |
-
<div class="
|
| 66 |
-
<div class="
|
| 67 |
-
<div class="
|
| 68 |
-
|
| 69 |
-
<
|
| 70 |
-
<div class="
|
| 71 |
-
<div class="
|
| 72 |
-
<
|
| 73 |
-
<div
|
| 74 |
-
<
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
<div
|
| 78 |
-
<div class="
|
| 79 |
-
<div class="
|
| 80 |
-
<div
|
| 81 |
-
<div class="
|
| 82 |
-
<div class="
|
| 83 |
-
<div class="
|
| 84 |
-
|
| 85 |
-
<
|
| 86 |
-
<div
|
| 87 |
-
<
|
| 88 |
-
|
| 89 |
-
<
|
| 90 |
-
|
| 91 |
-
<div class="
|
| 92 |
-
<
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
<div class="
|
| 97 |
-
<div class="
|
| 98 |
-
<div class="
|
| 99 |
-
<div
|
| 100 |
-
</div>
|
| 101 |
-
<
|
| 102 |
-
|
| 103 |
-
<div class="
|
| 104 |
-
<
|
| 105 |
-
<
|
| 106 |
-
<
|
| 107 |
-
|
| 108 |
-
<
|
| 109 |
-
<
|
| 110 |
-
<div
|
| 111 |
-
|
| 112 |
-
<
|
| 113 |
-
<
|
| 114 |
-
<
|
| 115 |
-
<
|
| 116 |
-
<div
|
| 117 |
-
<div class="
|
| 118 |
-
<div class="
|
| 119 |
-
<div
|
| 120 |
-
<
|
| 121 |
-
|
| 122 |
-
<
|
| 123 |
-
<
|
| 124 |
-
<
|
| 125 |
-
<
|
| 126 |
-
<div
|
| 127 |
-
|
| 128 |
-
<
|
| 129 |
-
<div class="
|
| 130 |
-
<div class="
|
| 131 |
-
|
| 132 |
-
<
|
| 133 |
-
|
| 134 |
-
<
|
| 135 |
-
|
| 136 |
-
<div
|
| 137 |
-
|
| 138 |
-
<div class="
|
| 139 |
-
|
| 140 |
-
<div
|
| 141 |
-
<
|
| 142 |
-
</div>
|
| 143 |
-
|
| 144 |
-
<
|
| 145 |
-
<
|
| 146 |
-
|
| 147 |
-
<
|
| 148 |
-
|
| 149 |
-
<div class="
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
<div class="
|
| 153 |
-
|
| 154 |
-
<div class="
|
| 155 |
-
</div>
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
<div class="
|
| 159 |
-
|
| 160 |
-
</div>
|
| 161 |
-
|
| 162 |
-
<div
|
| 163 |
-
|
| 164 |
-
<
|
| 165 |
-
<
|
| 166 |
-
</div>
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
<div
|
| 170 |
-
<div class="
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
<div class="
|
| 174 |
-
|
| 175 |
-
<div class="
|
| 176 |
-
</div>
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
<div
|
| 182 |
-
<
|
| 183 |
-
</div>
|
| 184 |
-
|
| 185 |
-
<
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
<
|
| 189 |
-
<div
|
| 190 |
-
<div class="
|
| 191 |
-
|
| 192 |
-
<div class="
|
| 193 |
-
<div class="
|
| 194 |
-
<div class="
|
| 195 |
-
<
|
| 196 |
-
<
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
<div class="
|
| 200 |
-
<div class="
|
| 201 |
-
<div
|
| 202 |
-
</div>
|
| 203 |
-
</div>
|
| 204 |
-
|
| 205 |
-
<
|
| 206 |
-
<div
|
| 207 |
-
<
|
| 208 |
-
|
| 209 |
-
<
|
| 210 |
-
<
|
| 211 |
-
<
|
| 212 |
-
<div
|
| 213 |
-
<
|
| 214 |
-
|
| 215 |
-
<div class="
|
| 216 |
-
|
| 217 |
-
<div class="
|
| 218 |
-
<div class="
|
| 219 |
-
<div class="
|
| 220 |
-
</div>
|
| 221 |
-
|
| 222 |
-
<
|
| 223 |
-
<div
|
| 224 |
-
<div class="
|
| 225 |
-
<div class="
|
| 226 |
-
<
|
| 227 |
-
<
|
| 228 |
-
</div>
|
| 229 |
-
|
| 230 |
-
<
|
| 231 |
-
<div
|
| 232 |
-
<div class="
|
| 233 |
-
|
| 234 |
-
<div
|
| 235 |
-
<
|
| 236 |
-
<div
|
| 237 |
-
<
|
| 238 |
-
<
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
</body>
|
| 244 |
-
|
|
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
| 3 |
+
|
| 4 |
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
| 7 |
+
<title>HCP Segmentation Dashboard β Pfizer UC</title>
|
| 8 |
+
<meta name="description"
|
| 9 |
+
content="Longitudinal behavioral analysis for Ulcerative Colitis HCP segmentation β powered by 191-column deep analytics">
|
| 10 |
+
<link rel="stylesheet" href="styles.css">
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 12 |
+
|
| 13 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
| 14 |
</head>
|
| 15 |
+
|
| 16 |
<body>
|
| 17 |
|
| 18 |
+
<!-- Top Header -->
|
| 19 |
+
<header class="top-header">
|
| 20 |
+
<div class="top-header-left">
|
| 21 |
+
<img src="Pfizer_Logo_Color_RGB.png" alt="Pfizer">
|
| 22 |
+
<h1>HCP Segmentation Insights</h1>
|
| 23 |
+
<span>Ulcerative Colitis β 191-Feature Deep Analytics</span>
|
| 24 |
+
</div>
|
| 25 |
+
<div><span class="header-badge">Pipeline Stable</span></div>
|
| 26 |
+
</header>
|
| 27 |
+
|
| 28 |
+
<!-- Tab Navigation -->
|
| 29 |
+
<nav class="tab-nav">
|
| 30 |
+
<button class="tab-btn active" data-tab="tab-overview"><i class="fas fa-chart-pie"></i> Executive
|
| 31 |
+
Summary</button>
|
| 32 |
+
<button class="tab-btn" data-tab="tab-segments"><i class="fas fa-layer-group"></i> Segment Deep-Dive</button>
|
| 33 |
+
<button class="tab-btn" data-tab="tab-adoption"><i class="fas fa-rocket"></i> Brand Adoption</button>
|
| 34 |
+
<button class="tab-btn" data-tab="tab-competitive"><i class="fas fa-chess"></i> Competitive Intel</button>
|
| 35 |
+
<button class="tab-btn" data-tab="tab-engagement"><i class="fas fa-handshake"></i> Rep Engagement</button>
|
| 36 |
+
<button class="tab-btn" data-tab="tab-opportunity"><i class="fas fa-crosshairs"></i> Unlabeled
|
| 37 |
+
Opportunity</button>
|
| 38 |
+
<button class="tab-btn" data-tab="tab-specialty"><i class="fas fa-stethoscope"></i> Specialty Mix</button>
|
| 39 |
+
|
| 40 |
+
</nav>
|
| 41 |
+
|
| 42 |
+
<!-- ==================== TAB 1: EXECUTIVE SUMMARY ==================== -->
|
| 43 |
+
<div id="tab-overview" class="tab-content active">
|
| 44 |
+
<div class="section-header">
|
| 45 |
+
<div class="section-icon"><i class="fas fa-chart-pie"></i></div>
|
| 46 |
+
<div>
|
| 47 |
+
<div class="section-title">Executive Summary</div>
|
| 48 |
+
<div class="section-subtitle">Real KPIs from 20,931 HCPs across 86-week longitudinal panel</div>
|
| 49 |
+
</div>
|
| 50 |
+
</div>
|
| 51 |
+
|
| 52 |
+
<div class="grid-5">
|
| 53 |
+
<div class="card kpi-card">
|
| 54 |
+
<div class="kpi-top"><span class="kpi-label">Total HCPs</span>
|
| 55 |
+
<div class="kpi-icon" style="background:#f1f5f9;color:#64748b"><i class="fas fa-users"></i></div>
|
| 56 |
+
</div>
|
| 57 |
+
<div class="kpi-value" data-count="20931">0</div>
|
| 58 |
+
<div class="kpi-sub">86-week longitudinal panel</div>
|
| 59 |
+
</div>
|
| 60 |
+
<div class="card kpi-card">
|
| 61 |
+
<div class="kpi-top"><span class="kpi-label">Labeled Cohort</span>
|
| 62 |
+
<div class="kpi-icon" style="background:#eef6fc;color:var(--pfizer-blue)"><i class="fas fa-tag"></i>
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
<div class="kpi-value" data-count="11899">0</div>
|
| 66 |
+
<div class="kpi-sub">56.9% of total market</div>
|
| 67 |
+
</div>
|
| 68 |
+
<div class="card kpi-card">
|
| 69 |
+
<div class="kpi-top"><span class="kpi-label">Unlabeled Pool</span>
|
| 70 |
+
<div class="kpi-icon" style="background:#f5f3ff;color:var(--seg-unlabeled)"><i
|
| 71 |
+
class="fas fa-search"></i></div>
|
| 72 |
+
</div>
|
| 73 |
+
<div class="kpi-value" data-count="633">0</div>
|
| 74 |
+
<div class="kpi-sub">Pending classification</div>
|
| 75 |
+
</div>
|
| 76 |
+
<div class="card kpi-card">
|
| 77 |
+
<div class="kpi-top"><span class="kpi-label">Feature Columns</span>
|
| 78 |
+
<div class="kpi-icon" style="background:#ecfaff;color:var(--pfizer-sky)"><i
|
| 79 |
+
class="fas fa-database"></i></div>
|
| 80 |
+
</div>
|
| 81 |
+
<div class="kpi-value" data-count="191">0</div>
|
| 82 |
+
<div class="kpi-sub">7 feature blocks engineered</div>
|
| 83 |
+
</div>
|
| 84 |
+
<div class="card kpi-card">
|
| 85 |
+
<div class="kpi-top"><span class="kpi-label">Deep Learning Recall</span>
|
| 86 |
+
<div class="kpi-icon" style="background:#ecfdf5;color:var(--accent-green)"><i
|
| 87 |
+
class="fas fa-brain"></i></div>
|
| 88 |
+
</div>
|
| 89 |
+
<div class="kpi-value" data-count="75" data-suffix="%">0</div>
|
| 90 |
+
<div class="kpi-sub">On minority class (SEG_C)</div>
|
| 91 |
+
</div>
|
| 92 |
+
</div>
|
| 93 |
+
|
| 94 |
+
<div class="alert-box alert-info" style="margin-top:20px"><i class="fas fa-info-circle"></i><span><strong>Data
|
| 95 |
+
Source:</strong> All metrics are computed from <code>hcp_analysis_clean.parquet</code> (20,931 HCPs
|
| 96 |
+
Γ 191 columns). KPIs reflect real aggregated prescribing, engagement, and market share data across the
|
| 97 |
+
86-week observation window.</span></div>
|
| 98 |
+
|
| 99 |
+
<div class="grid-2" style="margin-top:24px">
|
| 100 |
+
<div class="card">
|
| 101 |
+
<div class="chart-title">Population Distribution</div>
|
| 102 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-funnel"></canvas></div>
|
| 103 |
+
</div>
|
| 104 |
+
<div class="card">
|
| 105 |
+
<div class="chart-title">Labeled Target Breakdown</div>
|
| 106 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-doughnut"></canvas></div>
|
| 107 |
+
</div>
|
| 108 |
+
</div>
|
| 109 |
+
<div class="card" style="margin-top:24px">
|
| 110 |
+
<div class="chart-title">Key Metrics by Segment (Labeled Cohort)</div>
|
| 111 |
+
<div class="chart-container" style="height:340px"><canvas id="chart-heatmap"></canvas></div>
|
| 112 |
+
</div>
|
| 113 |
+
</div>
|
| 114 |
+
|
| 115 |
+
<!-- ==================== TAB 2: SEGMENT DEEP-DIVE ==================== -->
|
| 116 |
+
<div id="tab-segments" class="tab-content">
|
| 117 |
+
<div class="section-header">
|
| 118 |
+
<div class="section-icon"><i class="fas fa-layer-group"></i></div>
|
| 119 |
+
<div>
|
| 120 |
+
<div class="section-title">Segment Deep-Dive</div>
|
| 121 |
+
<div class="section-subtitle">Behavioral profiling from actual 191-column feature engineering</div>
|
| 122 |
+
</div>
|
| 123 |
+
</div>
|
| 124 |
+
|
| 125 |
+
<div class="grid-3">
|
| 126 |
+
<!-- Seg A -->
|
| 127 |
+
<div class="card segment-card seg-a">
|
| 128 |
+
<div class="segment-name" style="color:var(--seg-a)">Segment A β Traditional</div>
|
| 129 |
+
<div class="segment-desc">Lowest prescribing volume (0.17 UC TRx/wk). Only 3.8% show Pfizer growth.
|
| 130 |
+
Highest rep effort per Rx (0.94 details/TRx). Status-quo prescribers resistant to new therapies.
|
| 131 |
+
</div>
|
| 132 |
+
<div class="segment-stats">
|
| 133 |
+
<div class="segment-stat"><span class="segment-stat-label">Population</span><span
|
| 134 |
+
class="segment-stat-value">6,406 (53.8%)</span></div>
|
| 135 |
+
<div class="segment-stat"><span class="segment-stat-label">UC TRx/week</span><span
|
| 136 |
+
class="segment-stat-value">0.171</span></div>
|
| 137 |
+
<div class="segment-stat"><span class="segment-stat-label">Pfizer Share of UC</span><span
|
| 138 |
+
class="segment-stat-value" style="color:var(--accent-coral)">0.36%</span></div>
|
| 139 |
+
<div class="segment-stat"><span class="segment-stat-label">Active Weeks</span><span
|
| 140 |
+
class="segment-stat-value">46.1%</span></div>
|
| 141 |
+
<div class="segment-stat"><span class="segment-stat-label">Details per Rx</span><span
|
| 142 |
+
class="segment-stat-value" style="color:var(--accent-coral)">0.94</span></div>
|
| 143 |
+
<div style="margin-top:8px"><span class="badge badge-amber">Low Priority β Baseline</span></div>
|
| 144 |
+
</div>
|
| 145 |
+
</div>
|
| 146 |
+
|
| 147 |
+
<!-- Seg B -->
|
| 148 |
+
<div class="card segment-card seg-b">
|
| 149 |
+
<div class="segment-name" style="color:var(--seg-b)">Segment B β Relationship</div>
|
| 150 |
+
<div class="segment-desc">Highest Pfizer growth signal (9.6% growing). Most efficient rep conversion
|
| 151 |
+
(0.44 details/TRx). Broadest promo engagement (2.56 channels). The primary commercial target.</div>
|
| 152 |
+
<div class="segment-stats">
|
| 153 |
+
<div class="segment-stat"><span class="segment-stat-label">Population</span><span
|
| 154 |
+
class="segment-stat-value">3,349 (28.2%)</span></div>
|
| 155 |
+
<div class="segment-stat"><span class="segment-stat-label">UC TRx/week</span><span
|
| 156 |
+
class="segment-stat-value">0.517</span></div>
|
| 157 |
+
<div class="segment-stat"><span class="segment-stat-label">Pfizer Share of UC</span><span
|
| 158 |
+
class="segment-stat-value" style="color:var(--seg-b)">0.48%</span></div>
|
| 159 |
+
<div class="segment-stat"><span class="segment-stat-label">Active Weeks</span><span
|
| 160 |
+
class="segment-stat-value">73.3%</span></div>
|
| 161 |
+
<div class="segment-stat"><span class="segment-stat-label">Details per Rx</span><span
|
| 162 |
+
class="segment-stat-value" style="color:var(--accent-green)">0.44</span></div>
|
| 163 |
+
<div style="margin-top:8px"><span class="badge badge-green">High Priority β Target</span></div>
|
| 164 |
+
</div>
|
| 165 |
+
</div>
|
| 166 |
+
|
| 167 |
+
<!-- Seg C -->
|
| 168 |
+
<div class="card segment-card seg-c">
|
| 169 |
+
<div class="segment-name" style="color:var(--seg-c)">Segment C β Didactic</div>
|
| 170 |
+
<div class="segment-desc">Highest UC volume (0.71 TRx/wk) and strongest biologic loyalty (11.3% IL-23
|
| 171 |
+
share). Most efficient rep relationship (0.38 details/TRx). Protocol-driven, evidence-based HCPs.
|
| 172 |
+
</div>
|
| 173 |
+
<div class="segment-stats">
|
| 174 |
+
<div class="segment-stat"><span class="segment-stat-label">Population</span><span
|
| 175 |
+
class="segment-stat-value">2,144 (18.0%)</span></div>
|
| 176 |
+
<div class="segment-stat"><span class="segment-stat-label">UC TRx/week</span><span
|
| 177 |
+
class="segment-stat-value">0.711</span></div>
|
| 178 |
+
<div class="segment-stat"><span class="segment-stat-label">Pfizer Share of UC</span><span
|
| 179 |
+
class="segment-stat-value" style="color:var(--seg-c)">0.31%</span></div>
|
| 180 |
+
<div class="segment-stat"><span class="segment-stat-label">Active Weeks</span><span
|
| 181 |
+
class="segment-stat-value">76.8%</span></div>
|
| 182 |
+
<div class="segment-stat"><span class="segment-stat-label">Details per Rx</span><span
|
| 183 |
+
class="segment-stat-value" style="color:var(--accent-green)">0.38</span></div>
|
| 184 |
+
<div style="margin-top:8px"><span class="badge badge-sky">Upsell Target β Deep Learning Focus</span>
|
| 185 |
+
</div>
|
| 186 |
+
</div>
|
| 187 |
+
</div>
|
| 188 |
+
</div>
|
| 189 |
+
|
| 190 |
+
<div class="grid-2" style="margin-top:24px">
|
| 191 |
+
<div class="card">
|
| 192 |
+
<div class="chart-title">UC TRx Volume by Segment</div>
|
| 193 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-segment-bars"></canvas></div>
|
| 194 |
+
</div>
|
| 195 |
+
<div class="card">
|
| 196 |
+
<div class="chart-title">Medication Mix by Segment</div>
|
| 197 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-med-mix"></canvas></div>
|
| 198 |
+
</div>
|
| 199 |
+
</div>
|
| 200 |
+
|
| 201 |
+
<!-- HCP 86-Week Timelines (restored from V1) -->
|
| 202 |
+
<div class="section-header" style="margin-top:32px">
|
| 203 |
+
<div class="section-icon"><i class="fas fa-chart-line"></i></div>
|
| 204 |
+
<div>
|
| 205 |
+
<div class="section-title">HCP Longitudinal Journeys</div>
|
| 206 |
+
<div class="section-subtitle">Visualizing the 86-week sequential data fed into the tensors</div>
|
| 207 |
+
</div>
|
| 208 |
+
</div>
|
| 209 |
+
<div class="sub-tabs">
|
| 210 |
+
<button class="sub-tab active" data-subtab="sub-pb">Dr. James Chen (SEG_B)</button>
|
| 211 |
+
<button class="sub-tab" data-subtab="sub-pc">Dr. Sarah Williams (SEG_C)</button>
|
| 212 |
+
<button class="sub-tab" data-subtab="sub-pa">Dr. Maria Lopez (SEG_A)</button>
|
| 213 |
+
</div>
|
| 214 |
+
<!-- Dr. Chen (SEG_B) -->
|
| 215 |
+
<div id="sub-pb" class="sub-panel active">
|
| 216 |
+
<div class="card persona-card">
|
| 217 |
+
<div class="persona-header">
|
| 218 |
+
<div class="persona-avatar" style="background:var(--pfizer-light)">JC</div>
|
| 219 |
+
<div>
|
| 220 |
+
<div class="persona-name">Dr. James Chen</div>
|
| 221 |
+
<div class="persona-role">San Francisco, CA β Relationship-Centric</div>
|
| 222 |
+
<div class="persona-seg" style="color:var(--pfizer-light)">Segment B</div>
|
| 223 |
+
</div>
|
| 224 |
+
</div>
|
| 225 |
+
<div class="persona-detail-grid" style="margin-top:0;margin-bottom:24px">
|
| 226 |
+
<div class="persona-metric">
|
| 227 |
+
<div class="persona-metric-label">Avg Weekly TRx Volume</div>
|
| 228 |
+
<div class="persona-metric-value">14.2</div>
|
| 229 |
+
</div>
|
| 230 |
+
<div class="persona-metric">
|
| 231 |
+
<div class="persona-metric-label">86-Week Interactions</div>
|
| 232 |
+
<div class="persona-metric-value">High</div>
|
| 233 |
+
</div>
|
| 234 |
+
</div>
|
| 235 |
+
<div class="chart-container" style="height:320px"><canvas id="chart-pb-main"></canvas></div>
|
| 236 |
+
</div>
|
| 237 |
+
</div>
|
| 238 |
+
<!-- Dr. Williams (SEG_C) -->
|
| 239 |
+
<div id="sub-pc" class="sub-panel">
|
| 240 |
+
<div class="card persona-card">
|
| 241 |
+
<div class="persona-header">
|
| 242 |
+
<div class="persona-avatar" style="background:var(--pfizer-deep)">SW</div>
|
| 243 |
+
<div>
|
| 244 |
+
<div class="persona-name">Dr. Sarah Williams</div>
|
| 245 |
+
<div class="persona-role">Chicago, IL β Didactic / Cautious</div>
|
| 246 |
+
<div class="persona-seg" style="color:var(--pfizer-deep)">Segment C</div>
|
| 247 |
+
</div>
|
| 248 |
+
</div>
|
| 249 |
+
<div class="persona-detail-grid" style="margin-top:0;margin-bottom:24px">
|
| 250 |
+
<div class="persona-metric">
|
| 251 |
+
<div class="persona-metric-label">Avg Weekly TRx Volume</div>
|
| 252 |
+
<div class="persona-metric-value">6.8</div>
|
| 253 |
+
</div>
|
| 254 |
+
<div class="persona-metric">
|
| 255 |
+
<div class="persona-metric-label">86-Week Interactions</div>
|
| 256 |
+
<div class="persona-metric-value">Moderate</div>
|
| 257 |
+
</div>
|
| 258 |
+
</div>
|
| 259 |
+
<div class="chart-container" style="height:320px"><canvas id="chart-pc-main"></canvas></div>
|
| 260 |
+
</div>
|
| 261 |
+
</div>
|
| 262 |
+
<!-- Dr. Lopez (SEG_A) -->
|
| 263 |
+
<div id="sub-pa" class="sub-panel">
|
| 264 |
+
<div class="card persona-card">
|
| 265 |
+
<div class="persona-header">
|
| 266 |
+
<div class="persona-avatar" style="background:var(--pfizer-blue)">ML</div>
|
| 267 |
+
<div>
|
| 268 |
+
<div class="persona-name">Dr. Maria Lopez</div>
|
| 269 |
+
<div class="persona-role">Houston, TX β Traditional Prescriber</div>
|
| 270 |
+
<div class="persona-seg" style="color:var(--pfizer-blue)">Segment A</div>
|
| 271 |
+
</div>
|
| 272 |
+
</div>
|
| 273 |
+
<div class="persona-detail-grid" style="margin-top:0;margin-bottom:24px">
|
| 274 |
+
<div class="persona-metric">
|
| 275 |
+
<div class="persona-metric-label">Avg Weekly TRx Volume</div>
|
| 276 |
+
<div class="persona-metric-value">12.0</div>
|
| 277 |
+
</div>
|
| 278 |
+
<div class="persona-metric">
|
| 279 |
+
<div class="persona-metric-label">86-Week Interactions</div>
|
| 280 |
+
<div class="persona-metric-value">Low</div>
|
| 281 |
+
</div>
|
| 282 |
+
</div>
|
| 283 |
+
<div class="chart-container" style="height:320px"><canvas id="chart-pa-main"></canvas></div>
|
| 284 |
+
</div>
|
| 285 |
+
</div>
|
| 286 |
+
</div>
|
| 287 |
+
|
| 288 |
+
<!-- ==================== TAB 3: BRAND ADOPTION ==================== -->
|
| 289 |
+
<div id="tab-adoption" class="tab-content">
|
| 290 |
+
<div class="section-header">
|
| 291 |
+
<div class="section-icon"><i class="fas fa-rocket"></i></div>
|
| 292 |
+
<div>
|
| 293 |
+
<div class="section-title">Brand1 Adoption & Trajectory</div>
|
| 294 |
+
<div class="section-subtitle">Adoption funnel, growth signals, and recency trends from real data</div>
|
| 295 |
+
</div>
|
| 296 |
+
</div>
|
| 297 |
+
|
| 298 |
+
<div class="grid-3" style="margin-bottom:24px">
|
| 299 |
+
<div class="card metric-highlight">
|
| 300 |
+
<div class="metric-highlight-value" style="color:var(--accent-coral)">91.4%</div>
|
| 301 |
+
<div class="metric-highlight-label">Never Tried Brand1</div>
|
| 302 |
+
</div>
|
| 303 |
+
<div class="card metric-highlight">
|
| 304 |
+
<div class="metric-highlight-value" style="color:var(--accent-green)">5.0%</div>
|
| 305 |
+
<div class="metric-highlight-label">Currently Active</div>
|
| 306 |
+
</div>
|
| 307 |
+
<div class="card metric-highlight">
|
| 308 |
+
<div class="metric-highlight-value" style="color:var(--accent-amber)">2.6%</div>
|
| 309 |
+
<div class="metric-highlight-label">Trialed Then Lapsed</div>
|
| 310 |
+
</div>
|
| 311 |
+
</div>
|
| 312 |
+
|
| 313 |
+
<div class="grid-2">
|
| 314 |
+
<div class="card">
|
| 315 |
+
<div class="chart-title">Adoption Funnel by Segment (% of each segment)</div>
|
| 316 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-adoption-pct"></canvas></div>
|
| 317 |
+
</div>
|
| 318 |
+
<div class="card">
|
| 319 |
+
<div class="chart-title">Adoption Funnel by Segment (Absolute Count)</div>
|
| 320 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-adoption-abs"></canvas></div>
|
| 321 |
+
</div>
|
| 322 |
+
</div>
|
| 323 |
+
|
| 324 |
+
<div class="grid-2" style="margin-top:24px">
|
| 325 |
+
<div class="card">
|
| 326 |
+
<div class="chart-title">Growth Signals: Who Is Accelerating?</div>
|
| 327 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-growth-signals"></canvas></div>
|
| 328 |
+
</div>
|
| 329 |
+
<div class="card">
|
| 330 |
+
<div class="chart-title">Pfizer TRx: 86-Week Average vs. Recent 8 Weeks</div>
|
| 331 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-trend-bars"></canvas></div>
|
| 332 |
+
</div>
|
| 333 |
+
</div>
|
| 334 |
+
</div>
|
| 335 |
+
|
| 336 |
+
<!-- ==================== TAB 4: COMPETITIVE INTELLIGENCE ==================== -->
|
| 337 |
+
<div id="tab-competitive" class="tab-content">
|
| 338 |
+
<div class="section-header">
|
| 339 |
+
<div class="section-icon"><i class="fas fa-chess"></i></div>
|
| 340 |
+
<div>
|
| 341 |
+
<div class="section-title">Competitive Intelligence</div>
|
| 342 |
+
<div class="section-subtitle">Pfizer vs Brand2 market dynamics across segments</div>
|
| 343 |
+
</div>
|
| 344 |
+
</div>
|
| 345 |
+
|
| 346 |
+
<div class="grid-3" style="margin-bottom:24px">
|
| 347 |
+
<div class="card metric-highlight">
|
| 348 |
+
<div class="metric-highlight-value" style="color:var(--seg-a)">3.90Γ</div>
|
| 349 |
+
<div class="metric-highlight-label">Brand2/Pfizer β SEG_A</div>
|
| 350 |
+
</div>
|
| 351 |
+
<div class="card metric-highlight">
|
| 352 |
+
<div class="metric-highlight-value" style="color:var(--seg-b)">4.43Γ</div>
|
| 353 |
+
<div class="metric-highlight-label">Brand2/Pfizer β SEG_B</div>
|
| 354 |
+
</div>
|
| 355 |
+
<div class="card metric-highlight">
|
| 356 |
+
<div class="metric-highlight-value" style="color:var(--seg-c)">4.29Γ</div>
|
| 357 |
+
<div class="metric-highlight-label">Brand2/Pfizer β SEG_C</div>
|
| 358 |
+
</div>
|
| 359 |
+
</div>
|
| 360 |
+
|
| 361 |
+
<div class="alert-box alert-warning"><i class="fas fa-exclamation-triangle"></i><span><strong>Competitive
|
| 362 |
+
Pressure:</strong> Brand2 outprescribes Pfizer by 3.9β4.4Γ across all segments. The gap is widest in
|
| 363 |
+
SEG_B (4.43Γ), the very segment with the highest Pfizer growth trajectory β indicating an active
|
| 364 |
+
battleground for share.</span></div>
|
| 365 |
+
|
| 366 |
+
<div class="grid-2" style="margin-top:24px">
|
| 367 |
+
<div class="card">
|
| 368 |
+
<div class="chart-title">Pfizer vs Brand2: Market Share of UC (%) by Segment</div>
|
| 369 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-comp-share"></canvas></div>
|
| 370 |
+
</div>
|
| 371 |
+
<div class="card">
|
| 372 |
+
<div class="chart-title">Brand2/Pfizer Prescribing Ratio by Segment</div>
|
| 373 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-comp-ratio"></canvas></div>
|
| 374 |
+
</div>
|
| 375 |
+
</div>
|
| 376 |
+
|
| 377 |
+
<div class="card" style="margin-top:24px">
|
| 378 |
+
<div class="chart-title">UC TRx Volume vs Pfizer Market Share (Labeled HCPs)</div>
|
| 379 |
+
<div class="chart-container" style="height:420px"><canvas id="chart-scatter-uc"></canvas></div>
|
| 380 |
+
</div>
|
| 381 |
+
</div>
|
| 382 |
+
|
| 383 |
+
<!-- ==================== TAB 5: REP ENGAGEMENT ==================== -->
|
| 384 |
+
<div id="tab-engagement" class="tab-content">
|
| 385 |
+
<div class="section-header">
|
| 386 |
+
<div class="section-icon"><i class="fas fa-handshake"></i></div>
|
| 387 |
+
<div>
|
| 388 |
+
<div class="section-title">Rep Engagement ROI</div>
|
| 389 |
+
<div class="section-subtitle">Measuring commercial efficiency: visits, channels, and prescribing impact
|
| 390 |
+
</div>
|
| 391 |
+
</div>
|
| 392 |
+
</div>
|
| 393 |
+
|
| 394 |
+
<div class="grid-4" style="margin-bottom:24px">
|
| 395 |
+
<div class="card metric-highlight">
|
| 396 |
+
<div class="metric-highlight-value" style="color:var(--seg-a)">0.94</div>
|
| 397 |
+
<div class="metric-highlight-label">Details/Rx β SEG_A</div>
|
| 398 |
+
</div>
|
| 399 |
+
<div class="card metric-highlight">
|
| 400 |
+
<div class="metric-highlight-value" style="color:var(--seg-b)">0.44</div>
|
| 401 |
+
<div class="metric-highlight-label">Details/Rx β SEG_B</div>
|
| 402 |
+
</div>
|
| 403 |
+
<div class="card metric-highlight">
|
| 404 |
+
<div class="metric-highlight-value" style="color:var(--seg-c)">0.38</div>
|
| 405 |
+
<div class="metric-highlight-label">Details/Rx β SEG_C</div>
|
| 406 |
+
</div>
|
| 407 |
+
<div class="card metric-highlight">
|
| 408 |
+
<div class="metric-highlight-value" style="color:var(--accent-green)">2.1Γ</div>
|
| 409 |
+
<div class="metric-highlight-label">SEG_B is 2.1Γ more efficient than SEG_A</div>
|
| 410 |
+
</div>
|
| 411 |
+
</div>
|
| 412 |
+
|
| 413 |
+
<div class="grid-2">
|
| 414 |
+
<div class="card">
|
| 415 |
+
<div class="chart-title">Engagement Metrics by Segment</div>
|
| 416 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-engagement"></canvas></div>
|
| 417 |
+
</div>
|
| 418 |
+
<div class="card">
|
| 419 |
+
<div class="chart-title">Rep Visits vs Pfizer Prescribing (Scatter)</div>
|
| 420 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-scatter-eng"></canvas></div>
|
| 421 |
+
</div>
|
| 422 |
+
</div>
|
| 423 |
+
</div>
|
| 424 |
+
|
| 425 |
+
<!-- ==================== TAB 6: UNLABELED OPPORTUNITY ==================== -->
|
| 426 |
+
<div id="tab-opportunity" class="tab-content">
|
| 427 |
+
<div class="section-header">
|
| 428 |
+
<div class="section-icon"><i class="fas fa-crosshairs"></i></div>
|
| 429 |
+
<div>
|
| 430 |
+
<div class="section-title">Unlabeled HCP Opportunity</div>
|
| 431 |
+
<div class="section-subtitle">Prioritizing 633 unclassified HCPs for commercial outreach</div>
|
| 432 |
+
</div>
|
| 433 |
+
</div>
|
| 434 |
+
|
| 435 |
+
<div class="card" id="inference-card">
|
| 436 |
+
<div class="chart-title">Live Model Prediction: Segment Classification</div>
|
| 437 |
+
<div class="chart-container" style="height: auto; padding: 15px 0;">
|
| 438 |
+
<p style="font-size: 14px; color: var(--text-secondary); margin-bottom: 15px;">
|
| 439 |
+
Test the live Hugging Face model (SEG_A vs SEG_BC) with sample HCP data.
|
| 440 |
+
</p>
|
| 441 |
+
<button id="btn-predict" class="tab-btn active"
|
| 442 |
+
style="width: 100%; text-align: center; justify-content: center;">
|
| 443 |
+
<i class="fas fa-brain"></i> Run Live Prediction
|
| 444 |
+
</button>
|
| 445 |
+
<div id="prediction-result"
|
| 446 |
+
style="margin-top: 20px; font-weight: 600; color: var(--pfizer-deep); font-size: 16px; background: var(--bg-active); padding: 10px; border-radius: 8px; display: none;">
|
| 447 |
+
</div>
|
| 448 |
+
</div>
|
| 449 |
+
</div>
|
| 450 |
+
|
| 451 |
+
<div class="grid-3" style="margin-bottom:24px">
|
| 452 |
+
<div class="card tier-card tier-1">
|
| 453 |
+
<div class="tier-value" style="color:var(--accent-green)">43</div>
|
| 454 |
+
<div class="tier-label">Tier 1 β Immediate</div>
|
| 455 |
+
<div class="tier-desc">Score β₯ 0.60. Highest prescribing + growth signals.</div>
|
| 456 |
+
</div>
|
| 457 |
+
<div class="card tier-card tier-2">
|
| 458 |
+
<div class="tier-value" style="color:var(--accent-amber)">22</div>
|
| 459 |
+
<div class="tier-label">Tier 2 β Validate</div>
|
| 460 |
+
<div class="tier-desc">Score 0.35β0.60. Moderate opportunity, needs validation.</div>
|
| 461 |
+
</div>
|
| 462 |
+
<div class="card tier-card tier-3">
|
| 463 |
+
<div class="tier-value" style="color:var(--text-muted)">568</div>
|
| 464 |
+
<div class="tier-label">Tier 3 β Monitor</div>
|
| 465 |
+
<div class="tier-desc">Score < 0.35. Low activity, monitor for emergence.</div>
|
| 466 |
+
</div>
|
| 467 |
+
</div>
|
| 468 |
+
|
| 469 |
+
<div class="alert-box alert-warning"><i class="fas fa-exclamation-triangle"></i><span><strong>Coverage
|
| 470 |
+
Gap:</strong> 347 of 633 unlabeled HCPs (54.8%) have zero rep visits. Among Tier 1
|
| 471 |
+
(high-opportunity) HCPs, many prescribe actively but have never been contacted by a sales
|
| 472 |
+
representative.</span></div>
|
| 473 |
+
|
| 474 |
+
<div class="grid-2" style="margin-top:24px">
|
| 475 |
+
<div class="card">
|
| 476 |
+
<div class="chart-title">Opportunity Score Distribution (633 Unlabeled HCPs)</div>
|
| 477 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-opp-hist"></canvas></div>
|
| 478 |
+
</div>
|
| 479 |
+
<div class="card">
|
| 480 |
+
<div class="chart-title">Click a red point to identify the HCP below β</div>
|
| 481 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-opp-scatter"></canvas></div>
|
| 482 |
+
</div>
|
| 483 |
+
</div>
|
| 484 |
+
|
| 485 |
+
<!-- HCP Detail Panel (populated on click) -->
|
| 486 |
+
<div id="hcp-detail-panel" class="card"
|
| 487 |
+
style="margin-top:24px;display:none;border-left:4px solid var(--accent-coral)">
|
| 488 |
+
<div class="section-header" style="margin-bottom:16px">
|
| 489 |
+
<div class="section-icon" style="background:#fef2f2;color:var(--accent-coral)"><i
|
| 490 |
+
class="fas fa-user-md"></i></div>
|
| 491 |
+
<div>
|
| 492 |
+
<div class="section-title" id="hcp-detail-title">HCP Selected</div>
|
| 493 |
+
<div class="section-subtitle">Zero rep visits β high opportunity for outreach</div>
|
| 494 |
+
</div>
|
| 495 |
+
</div>
|
| 496 |
+
<div class="grid-5" id="hcp-detail-grid"></div>
|
| 497 |
+
<div class="alert-box alert-warning" style="margin-top:16px"><i
|
| 498 |
+
class="fas fa-bullhorn"></i><span><strong>Action Required:</strong> This HCP has never been
|
| 499 |
+
visited by a sales representative yet shows significant UC prescribing activity. Recommend
|
| 500 |
+
scheduling an initial detail call.</span></div>
|
| 501 |
+
</div>
|
| 502 |
+
</div>
|
| 503 |
+
|
| 504 |
+
<!-- ==================== TAB 7: SPECIALTY MIX ==================== -->
|
| 505 |
+
<div id="tab-specialty" class="tab-content">
|
| 506 |
+
<div class="section-header">
|
| 507 |
+
<div class="section-icon"><i class="fas fa-stethoscope"></i></div>
|
| 508 |
+
<div>
|
| 509 |
+
<div class="section-title">Specialty & Demographics</div>
|
| 510 |
+
<div class="section-subtitle">HCP specialty distribution across segments</div>
|
| 511 |
+
</div>
|
| 512 |
+
</div>
|
| 513 |
+
|
| 514 |
+
<div class="grid-2">
|
| 515 |
+
<div class="card">
|
| 516 |
+
<div class="chart-title">HCPs by Specialty and Segment (Stacked)</div>
|
| 517 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-spec-stack"></canvas></div>
|
| 518 |
+
</div>
|
| 519 |
+
<div class="card">
|
| 520 |
+
<div class="chart-title">Specialty Composition (% within each specialty)</div>
|
| 521 |
+
<div class="chart-container" style="height:300px"><canvas id="chart-spec-pct"></canvas></div>
|
| 522 |
+
</div>
|
| 523 |
+
</div>
|
| 524 |
+
</div>
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
|
| 528 |
+
<script src="app.js"></script>
|
| 529 |
</body>
|
| 530 |
+
|
| 531 |
+
</html>
|