NeoScan_Physician/app/modules/Dashboard/components/FeedbackAnalysisPieChart.tsx
2025-08-18 20:30:00 +05:30

338 lines
9.1 KiB
TypeScript

/*
* File: FeedbackAnalysisPieChart.tsx
* Description: Pie chart component for feedback analysis using react-native-chart-kit
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
import { PieChart } from 'react-native-chart-kit';
import { theme } from '../../../theme/theme';
// ============================================================================
// TYPES
// ============================================================================
/**
* Feedback Analysis Data Interface
*
* Purpose: Defines the structure of feedback analysis data for pie chart
*/
interface FeedbackAnalysisData {
positive: number;
negative: number;
total: number;
}
/**
* FeedbackAnalysisPieChart Props Interface
*
* Purpose: Defines the props required by the FeedbackAnalysisPieChart component
*
* Props:
* - data: Feedback analysis data containing positive, negative, and total counts
* - title: Optional title for the chart
* - width: Chart width (defaults to screen width - 32)
* - height: Chart height (defaults to 220)
*/
interface FeedbackAnalysisPieChartProps {
data: FeedbackAnalysisData;
title?: string;
width?: number;
height?: number;
}
// ============================================================================
// COMPONENT
// ============================================================================
/**
* FeedbackAnalysisPieChart Component
*
* Purpose: Renders a pie chart showing feedback analysis distribution
*
* Features:
* - Pie chart visualization of positive vs negative feedback
* - Custom colors for different feedback types
* - Responsive sizing
* - Legend with percentages
* - Empty state handling
*/
export const FeedbackAnalysisPieChart: React.FC<FeedbackAnalysisPieChartProps> = ({
data,
title = 'Feedback Analysis Overview',
width = Dimensions.get('window').width - 32,
height = 220,
}) => {
// ============================================================================
// DATA PROCESSING
// ============================================================================
/**
* Process data for pie chart
*
* Purpose: Convert feedback data into chart-kit format
*/
const chartData = React.useMemo(() => {
const { positive, negative } = data;
// Only show data if there are actual feedbacks
if (positive === 0 && negative === 0) {
return [];
}
const chartDataArray = [];
// Add positive feedback data
if (positive > 0) {
chartDataArray.push({
name: 'Positive',
population: positive,
color: theme.colors.success,
legendFontColor: theme.colors.textPrimary,
legendFontSize: 12,
});
}
// Add negative feedback data
if (negative > 0) {
chartDataArray.push({
name: 'Negative',
population: negative,
color: theme.colors.error,
legendFontColor: theme.colors.textPrimary,
legendFontSize: 12,
});
}
return chartDataArray;
}, [data]);
// ============================================================================
// CHART CONFIGURATION
// ============================================================================
/**
* Chart configuration object
*
* Purpose: Configure pie chart appearance and behavior
*/
const chartConfig = {
backgroundColor: theme.colors.background,
backgroundGradientFrom: theme.colors.background,
backgroundGradientTo: theme.colors.background,
decimalPlaces: 0,
color: (opacity = 1) => theme.colors.primary,
labelColor: (opacity = 1) => theme.colors.textPrimary,
style: {
borderRadius: theme.borderRadius.medium,
},
propsForDots: {
r: '6',
strokeWidth: '2',
stroke: theme.colors.primary,
},
};
// ============================================================================
// RENDER FUNCTIONS
// ============================================================================
/**
* Render empty state
*
* Purpose: Show message when no feedback data is available
*/
const renderEmptyState = () => (
<View style={styles.emptyState}>
<Text style={styles.emptyStateText}>No feedback data available</Text>
<Text style={styles.emptyStateSubtext}>
Feedback will appear here once received
</Text>
</View>
);
/**
* Render chart legend
*
* Purpose: Display custom legend with percentages
*/
const renderLegend = () => {
const { positive, negative, total } = data;
if (total === 0) return null;
const positivePercentage = ((positive / total) * 100).toFixed(1);
const negativePercentage = ((negative / total) * 100).toFixed(1);
return (
<View style={styles.legendContainer}>
<View style={styles.legendItem}>
<View style={[styles.legendColor, { backgroundColor: theme.colors.success }]} />
<Text style={styles.legendText}>
Positive: {positive} ({positivePercentage}%)
</Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendColor, { backgroundColor: theme.colors.error }]} />
<Text style={styles.legendText}>
Negative: {negative} ({negativePercentage}%)
</Text>
</View>
<View style={styles.totalContainer}>
<Text style={styles.totalText}>
Total Feedback: {total}
</Text>
</View>
</View>
);
};
// ============================================================================
// MAIN RENDER
// ============================================================================
return (
<View style={styles.container}>
{/* Chart Title */}
{/* {title && (
<Text style={styles.title}>{title}</Text>
)} */}
{/* Chart Container */}
<View style={styles.chartContainer}>
{chartData.length > 0 ? (
<>
{/* Pie Chart */}
<PieChart
data={chartData}
width={width}
height={height}
chartConfig={chartConfig}
accessor="population"
backgroundColor="transparent"
paddingLeft="0"
center={[width/4, 0]}
absolute
hasLegend={false}
/>
{/* Custom Legend */}
{renderLegend()}
</>
) : (
renderEmptyState()
)}
</View>
</View>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
alignItems: 'center',
justifyContent: 'center',
minHeight: 250,
},
// Chart title styling
title: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.md,
textAlign: 'center',
},
// Chart container
chartContainer: {
alignItems: 'center',
justifyContent: 'center',
width: '100%',
flex: 1,
},
// Empty state styling
emptyState: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: theme.spacing.xl,
minHeight: 150,
},
// Empty state text styling
emptyStateText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// Empty state subtext styling
emptyStateSubtext: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
},
// Legend container styling
legendContainer: {
marginTop: theme.spacing.md,
alignItems: 'center',
},
// Legend item styling
legendItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
// Legend color indicator styling
legendColor: {
width: 16,
height: 16,
borderRadius: 8,
marginRight: theme.spacing.sm,
},
// Legend text styling
legendText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
},
// Total container styling
totalContainer: {
marginTop: theme.spacing.sm,
paddingTop: theme.spacing.sm,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
alignItems: 'center',
},
// Total text styling
totalText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
});
/*
* End of File: FeedbackAnalysisPieChart.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/