226 lines
8.1 KiB
JavaScript
226 lines
8.1 KiB
JavaScript
import React, { useState } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import {
|
|
Box,
|
|
IconButton,
|
|
Paper,
|
|
Typography,
|
|
useTheme,
|
|
useMediaQuery,
|
|
Menu,
|
|
MenuItem,
|
|
TextField,
|
|
Button
|
|
} from '@mui/material';
|
|
import {
|
|
AreaChart,
|
|
Area,
|
|
XAxis,
|
|
YAxis,
|
|
Tooltip,
|
|
Legend,
|
|
ResponsiveContainer,
|
|
CartesianGrid
|
|
} from 'recharts';
|
|
// import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
|
|
const StatisticsCard = ({
|
|
title = "Statistics",
|
|
subtitle = "Delivery Times",
|
|
data = [],
|
|
dataKeys = [
|
|
{ key: 'revenue', name: 'Revenue', color: '#E46A11' },
|
|
{ key: 'sales', name: 'Sales', color: '#0182FC' }
|
|
],
|
|
xDataKey = 'month',
|
|
valueFormatter = (value) => value,
|
|
timeFrame = 'month',
|
|
onTimeFrameChange
|
|
}) => {
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
|
const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'md'));
|
|
const [selectedDate, setSelectedDate] = useState('');
|
|
const [anchorEl, setAnchorEl] = useState(null);
|
|
|
|
const handleMenuOpen = (event) => {
|
|
setAnchorEl(event.currentTarget);
|
|
};
|
|
const handleMenuClose = () => {
|
|
setAnchorEl(null);
|
|
};
|
|
|
|
const handleDateChange = (event) => {
|
|
setSelectedDate(event.target.value);
|
|
if (onTimeFrameChange) onTimeFrameChange(timeFrame, event.target.value);
|
|
handleMenuClose();
|
|
};
|
|
|
|
const handleTodayClick = () => {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
setSelectedDate(today);
|
|
if (onTimeFrameChange) onTimeFrameChange(timeFrame, today);
|
|
handleMenuClose();
|
|
};
|
|
|
|
// حساب العلامات الديناميكية للمحور الشاقولي
|
|
const ticksArray = (() => {
|
|
if (!data || data.length === 0) return [];
|
|
const maxValue = Math.max(
|
|
...data.flatMap(d => dataKeys.map(k => d[k.key] || 0))
|
|
);
|
|
const desiredTicks = 8; // عدد العلامات المطلوب
|
|
const step = Math.ceil(maxValue / desiredTicks) || 1;
|
|
const arr = [];
|
|
for (let i = 0; i <= maxValue; i += step) {
|
|
arr.push(i);
|
|
}
|
|
return arr;
|
|
})();
|
|
|
|
return (
|
|
<Box sx={{ borderRadius: 2, width: { sm: '100%', md: '167vh' } }}>
|
|
<Paper sx={{
|
|
p: { xs: 1.5, sm: 2 },
|
|
mb: { xs: 2, sm: 2 },
|
|
boxShadow: '0px 4px 5px rgba(0, 0, 0, 0.1)',
|
|
borderRadius: 2,
|
|
position: 'relative'
|
|
}}>
|
|
<IconButton
|
|
aria-label="more-actions"
|
|
sx={{
|
|
position: 'absolute',
|
|
top: { xs: 4, sm: 8 },
|
|
right: { xs: 4, sm: 8 },
|
|
color: '#667085'
|
|
}}
|
|
onClick={handleMenuOpen}
|
|
>
|
|
{/* <MoreVertIcon fontSize={isMobile ? 'small' : 'medium'} /> */}
|
|
</IconButton>
|
|
|
|
<Menu
|
|
anchorEl={anchorEl}
|
|
open={Boolean(anchorEl)}
|
|
onClose={handleMenuClose}
|
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
|
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
|
>
|
|
<MenuItem>
|
|
<TextField
|
|
type="date"
|
|
value={selectedDate}
|
|
onChange={handleDateChange}
|
|
size="small"
|
|
sx={{ width: 150 }}
|
|
/>
|
|
</MenuItem>
|
|
<MenuItem>
|
|
<Button variant="outlined" size="small" onClick={handleTodayClick}>
|
|
Today
|
|
</Button>
|
|
</MenuItem>
|
|
</Menu>
|
|
|
|
{/* Header */}
|
|
<Box sx={{
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
mb: 2,
|
|
flexWrap: 'wrap',
|
|
gap: 1
|
|
}}>
|
|
<Box>
|
|
<Typography variant="h6" sx={{ fontSize: { xs: '15px', sm: '18px' }, fontWeight: 500 }}>
|
|
{title}
|
|
</Typography>
|
|
<Typography sx={{ fontSize: { xs: '11px', sm: '14px' }, fontWeight: 500, color: '#667085' }}>
|
|
{subtitle}
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Chart */}
|
|
<ResponsiveContainer width="100%" height={isMobile ? 350 : isTablet ? 390 : 395}>
|
|
<AreaChart data={data} margin={{
|
|
top: isMobile ? -30 : -45,
|
|
right: isMobile ? 15 : 30,
|
|
left: isMobile ? 10 : 20,
|
|
bottom: isMobile ? 10 : 20
|
|
}}>
|
|
<defs>
|
|
{dataKeys.map(({ key, color }) => (
|
|
<linearGradient key={key} id={`color-${key}`} x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="0%" stopColor={color} stopOpacity={0.8} />
|
|
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
|
</linearGradient>
|
|
))}
|
|
</defs>
|
|
|
|
<CartesianGrid stroke="#eee" strokeDasharray="0 0" vertical={false} />
|
|
<XAxis
|
|
dataKey={xDataKey}
|
|
axisLine={false}
|
|
tickLine={false}
|
|
tickMargin={isMobile ? 8 : 15}
|
|
tick={{
|
|
fontSize: isMobile ? 11 : 12,
|
|
angle: -25,
|
|
textAnchor: 'end'
|
|
}}
|
|
interval={0}
|
|
/>
|
|
<YAxis
|
|
ticks={ticksArray}
|
|
tickFormatter={valueFormatter}
|
|
axisLine={false}
|
|
tickLine={false}
|
|
tickMargin={isMobile ? 8 : 15}
|
|
tick={{ fontSize: isMobile ? 11 : 12 }}
|
|
/>
|
|
|
|
<Tooltip formatter={valueFormatter} />
|
|
<Legend
|
|
verticalAlign="top"
|
|
height={isMobile ? 30 : 36}
|
|
iconType="circle"
|
|
iconSize={isMobile ? 8 : 10}
|
|
wrapperStyle={{ paddingBottom: isMobile ? '15px' : '20px' }}
|
|
/>
|
|
{dataKeys.map(({ key, name, color }) => (
|
|
<Area
|
|
key={key}
|
|
name={name}
|
|
type="monotone"
|
|
dataKey={key}
|
|
stroke={color}
|
|
strokeWidth={isMobile ? 2 : 3}
|
|
fill={`url(#color-${key})`}
|
|
/>
|
|
))}
|
|
</AreaChart>
|
|
</ResponsiveContainer>
|
|
</Paper>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
StatisticsCard.propTypes = {
|
|
title: PropTypes.string,
|
|
subtitle: PropTypes.string,
|
|
data: PropTypes.arrayOf(PropTypes.object),
|
|
dataKeys: PropTypes.arrayOf(PropTypes.shape({
|
|
key: PropTypes.string.isRequired,
|
|
name: PropTypes.string.isRequired,
|
|
color: PropTypes.string.isRequired
|
|
})),
|
|
xDataKey: PropTypes.string,
|
|
valueFormatter: PropTypes.func,
|
|
timeFrame: PropTypes.oneOf(['day', 'week', 'month', 'year']),
|
|
onTimeFrameChange: PropTypes.func
|
|
};
|
|
|
|
export default StatisticsCard;
|