@@ -8,6 +8,7 @@ import Input from 'components/input';
88import Tabs from 'components/tabs' ;
99import Reactive from 'html-tag-js/reactive' ;
1010import Ref from 'html-tag-js/ref' ;
11+ import { createChartSafely , drawBarValueLabels , drawDoughnutPercentLabels } from 'lib/dashboardCharts' ;
1112import { convertInrToUsd , formatCompactNumber , formatCompactUsd , formatExactNumber , formatExactUsd } from 'lib/formatNumber' ;
1213import { getLoggedInUser } from 'lib/helpers' ;
1314import moment from 'moment' ;
@@ -453,28 +454,46 @@ function Dashboard() {
453454 </ div > ,
454455 ) ;
455456
456- initChart ( revenueCanvas , lineChartConfig ( analytics . monthlyRevenue , 'Revenue (INR)' , '#22c55e' , true ) ) ;
457- initChart ( paymentsCanvas , lineChartConfig ( analytics . monthlyPayments , 'Payments (INR)' , '#3b82f6' , true ) ) ;
458- initChart ( topDevCanvas , horizontalBarChartConfig ( analytics . topDevelopers , 'name' , 'total' ) ) ;
459- initChart ( providerStatusCanvas , providerStatusChartConfig ( analytics . providerStatus ) ) ;
460- initChart ( paymentStatusCanvas , doughnutChartConfig ( analytics . paymentStatus , 'status' , 'count' ) ) ;
461- initChart ( editorCanvas , doughnutChartConfig ( analytics . editorDistribution , 'editor' , 'count' ) ) ;
462- } catch {
457+ initChart ( revenueCanvas , ( ) => lineChartConfig ( analytics . monthlyRevenue , 'Revenue (INR)' , '#22c55e' , true ) , 'Monthly Revenue' ) ;
458+ initChart ( paymentsCanvas , ( ) => lineChartConfig ( analytics . monthlyPayments , 'Payments (INR)' , '#3b82f6' , true ) , 'Monthly Payments' ) ;
459+ initChart ( topDevCanvas , ( ) => horizontalBarChartConfig ( analytics . topDevelopers , 'name' , 'total' ) , 'Top Developers' ) ;
460+ initChart ( providerStatusCanvas , ( ) => providerStatusChartConfig ( analytics . providerStatus ) , 'Orders by Provider and Status' ) ;
461+ initChart ( paymentStatusCanvas , ( ) => doughnutChartConfig ( analytics . paymentStatus , 'status' , 'count' ) , 'Payment Status' ) ;
462+ initChart ( editorCanvas , ( ) => doughnutChartConfig ( analytics . editorDistribution , 'editor' , 'count' ) , 'Editor Distribution' ) ;
463+ } catch ( error ) {
464+ console . error ( 'Failed to load dashboard data' , error ) ;
463465 ref . innerHTML = '<div class="error">Failed to load dashboard data</div>' ;
464466 }
465467 } ) ( ) ;
466468
467469 return < div ref = { ref } className = 'admin-dashboard' /> ;
468470}
469471
470- function initChart ( canvasRef , config ) {
472+ function initChart ( canvasRef , createConfig , label ) {
471473 let instance = null ;
472474 canvasRef . onref = ( ) => {
473- if ( instance ) instance . destroy ( ) ;
474- instance = new Chart ( canvasRef . el , config ) ;
475+ instance = createChartSafely ( {
476+ previousChart : instance ,
477+ createChart : ( ) => new Chart ( canvasRef . el , createConfig ( ) ) ,
478+ onError : ( error ) => {
479+ console . error ( `Failed to render ${ label } chart` , error ) ;
480+ showChartFallback ( canvasRef . el ) ;
481+ } ,
482+ } ) ;
475483 } ;
476484}
477485
486+ function showChartFallback ( canvas ) {
487+ const container = canvas ?. parentElement ;
488+ if ( ! container ) return ;
489+
490+ const message = document . createElement ( 'div' ) ;
491+ message . className = 'chart-error' ;
492+ message . textContent = 'Chart unavailable' ;
493+ container . classList . add ( 'chart-container--error' ) ;
494+ container . replaceChildren ( message ) ;
495+ }
496+
478497function lineChartConfig ( rows , label , color = '#3b82f6' , currency = false ) {
479498 const months = [ ] ;
480499 const now = new Date ( ) ;
@@ -552,26 +571,7 @@ function doughnutChartConfig(rows, labelKey, valueKey) {
552571 {
553572 id : 'doughnutPercentLabels' ,
554573 afterDatasetsDraw ( chart ) {
555- const { ctx, data : chartData } = chart ;
556- const dataset = chartData . datasets [ 0 ] ;
557- const meta = chart . getDatasetMeta ( 0 ) ;
558- ctx . save ( ) ;
559- ctx . font = 'bold 14px sans-serif' ;
560- ctx . textAlign = 'center' ;
561- ctx . textBaseline = 'middle' ;
562- for ( let i = 0 ; i < dataset . data . length ; i ++ ) {
563- const value = dataset . data [ i ] ;
564- if ( value === 0 ) continue ;
565- const pct = Math . round ( ( value / total ) * 100 ) ;
566- const arc = meta . data [ i ] ;
567- const { x, y } = arc . tooltipPosition ( true ) ;
568- ctx . fillStyle = '#ffffff' ;
569- ctx . shadowColor = 'rgba(0,0,0,0.6)' ;
570- ctx . shadowBlur = 3 ;
571- ctx . fillText ( `${ pct } %` , x , y ) ;
572- ctx . shadowBlur = 0 ;
573- }
574- ctx . restore ( ) ;
574+ drawDoughnutPercentLabels ( chart , total ) ;
575575 } ,
576576 } ,
577577 ] ,
@@ -693,25 +693,7 @@ function providerStatusChartConfig(rows) {
693693 {
694694 id : 'barValueLabels' ,
695695 afterDatasetsDraw ( chart ) {
696- const { ctx } = chart ;
697- ctx . save ( ) ;
698- ctx . font = 'bold 11px sans-serif' ;
699- ctx . textAlign = 'center' ;
700- ctx . textBaseline = 'bottom' ;
701- ctx . fillStyle = '#ffffff' ;
702- ctx . shadowColor = 'rgba(0,0,0,0.5)' ;
703- ctx . shadowBlur = 2 ;
704- for ( const ds of chart . data . datasets ) {
705- const meta = chart . getDatasetMeta ( chart . data . datasets . indexOf ( ds ) ) ;
706- for ( let i = 0 ; i < ds . data . length ; i ++ ) {
707- const value = ds . data [ i ] ;
708- if ( ! value ) continue ;
709- const { x, y } = meta . data [ i ] ;
710- ctx . fillText ( formatCompactNumber ( value ) , x , y - 2 ) ;
711- }
712- }
713- ctx . shadowBlur = 0 ;
714- ctx . restore ( ) ;
696+ drawBarValueLabels ( chart , formatCompactNumber ) ;
715697 } ,
716698 } ,
717699 ] ,
0 commit comments