Files
khata-ui/src/components/HistoryChart.tsx

131 lines
4.2 KiB
TypeScript

import * as React from "react";
import { Box, Typography, ToggleButtonGroup, ToggleButton, Paper } from "@mui/material";
export interface ChartDataPoint {
id: string;
amount: number;
count?: number;
highlighted?: boolean;
}
export interface HistoryChartProps {
header: string;
summary?: string;
tabs: string[];
data: Record<string, ChartDataPoint[]>;
}
export default function HistoryChart({
header,
summary,
tabs,
data,
}: HistoryChartProps) {
const [activeTab, setActiveTab] = React.useState<string>(tabs[0] || "");
const handleTabChange = (_: React.MouseEvent<HTMLElement>, newTab: string | null) => {
if (newTab !== null) {
setActiveTab(newTab);
}
};
const activeDataKey = activeTab.toLowerCase();
const currentData = data[activeDataKey] || data[activeTab] || [];
const maxAmount = Math.max(...currentData.map((d) => d.amount), 1);
return (
<Paper sx={{ p: { xs: 2, sm: 4 }, borderRadius: 4, width: "100%", boxShadow: 'none', border: '1px solid', borderColor: 'divider' }}>
<Typography variant="h6" fontWeight={700} gutterBottom>
{header}
</Typography>
{summary && (
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
{summary}
</Typography>
)}
<ToggleButtonGroup
value={activeTab}
exclusive
onChange={handleTabChange}
fullWidth
sx={{
mb: 4,
bgcolor: (theme) => theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.02)',
borderRadius: 8,
p: 0.5,
"& .MuiToggleButton-root": {
border: "none",
borderRadius: 8,
textTransform: "capitalize",
fontWeight: 600,
color: "text.secondary",
"&.Mui-selected": {
bgcolor: (theme) => theme.palette.mode === 'dark' ? 'primary.dark' : 'primary.light',
color: (theme) => theme.palette.mode === 'dark' ? 'primary.contrastText' : 'primary.main',
boxShadow: '0 2px 8px rgba(0,0,0,0.05)',
},
},
}}
>
{tabs.map((tab) => (
<ToggleButton key={tab} value={tab}>
{tab}
</ToggleButton>
))}
</ToggleButtonGroup>
{/* Chart Area */}
{currentData.length > 0 ? (
<Box sx={{ display: "flex", alignItems: "flex-end", height: 200, mt: 4, position: 'relative' }}>
{currentData.map((point) => {
const heightPerc = (point.amount / maxAmount) * 100;
return (
<Box
key={point.id}
sx={{
flex: 1,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "flex-end",
height: "100%",
}}
>
<Typography variant="caption" sx={{ mb: 1, opacity: 0.7, fontSize: '0.65rem', display: { xs: 'none', sm: 'block' } }}>
{point.amount > 0 ? `Rs ${point.amount}` : ''}
</Typography>
<Box
sx={{
width: "40%",
minWidth: 12,
maxWidth: 32,
height: `${heightPerc}%`,
minHeight: "4px",
bgcolor: point.highlighted ? "error.main" : "grey.300",
borderRadius: 4,
transition: "height 0.5s cubic-bezier(0.4, 0, 0.2, 1)",
...(point.highlighted && {
boxShadow: (theme) => `0 4px 12px ${theme.palette.error.main}40`,
}),
}}
/>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, fontWeight: 500, fontSize: '0.7rem' }}>
{point.id}
</Typography>
</Box>
);
})}
</Box>
) : (
<Box sx={{ height: 200, display: "flex", alignItems: "center", justifyContent: "center" }}>
<Typography color="text.secondary">No Data Available</Typography>
</Box>
)}
</Paper>
);
}