Files
fibdash/client/src/components/SummaryHeader.js

270 lines
8.8 KiB
JavaScript

import React, { Component } from 'react';
import {
Box,
Paper,
Typography,
Select,
MenuItem,
FormControl,
InputLabel,
Grid,
Button,
ListSubheader,
Divider,
} from '@mui/material';
import {
Download as DownloadIcon,
CalendarToday as CalendarIcon,
DateRange as QuarterIcon,
Event as YearIcon,
} from '@mui/icons-material';
class SummaryHeader extends Component {
formatAmount = (amount) => {
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(amount);
};
getMonthName = (monthYear) => {
if (!monthYear) return '';
const [year, month] = monthYear.split('-');
const date = new Date(year, month - 1);
return date.toLocaleDateString('de-DE', { month: 'long', year: 'numeric' });
};
getQuarterName = (year, quarter) => {
return `Q${quarter} ${year}`;
};
getYearName = (year) => {
return `Jahr ${year}`;
};
generateTimeRangeOptions = (months) => {
if (!months || months.length === 0) return { months: [], quarters: [], years: [] };
// Extract years from months
const years = [...new Set(months.map(month => month.split('-')[0]))].sort().reverse();
// Generate quarters
const quarters = [];
years.forEach(year => {
for (let q = 4; q >= 1; q--) {
const quarterMonths = this.getQuarterMonths(year, q);
// Only include quarter if we have data for at least one month in it
if (quarterMonths.some(month => months.includes(month))) {
quarters.push({ year, quarter: q, value: `${year}-Q${q}` });
}
}
});
return {
months: months,
quarters: quarters,
years: years.map(year => ({ year, value: year }))
};
};
getQuarterMonths = (year, quarter) => {
const startMonth = (quarter - 1) * 3 + 1;
return [
`${year}-${startMonth.toString().padStart(2, '0')}`,
`${year}-${(startMonth + 1).toString().padStart(2, '0')}`,
`${year}-${(startMonth + 2).toString().padStart(2, '0')}`
];
};
getSelectedDisplayName = (selectedValue, timeRangeOptions) => {
if (!selectedValue) return '';
if (selectedValue.includes('-Q')) {
const [year, quarterPart] = selectedValue.split('-Q');
return this.getQuarterName(year, quarterPart);
} else if (selectedValue.length === 4) {
return this.getYearName(selectedValue);
} else {
return this.getMonthName(selectedValue);
}
};
render() {
const {
months,
selectedMonth,
summary,
loading,
onMonthChange
} = this.props;
if (!summary) return null;
const timeRangeOptions = this.generateTimeRangeOptions(months);
return (
<Paper elevation={1} sx={{ p: { xs: 1.5, sm: 2 }, mb: 2 }}>
<Grid container alignItems="center" spacing={{ xs: 1, sm: 2 }}>
<Grid item xs={12} md={3}>
<FormControl fullWidth size="small">
<InputLabel>Zeitraum</InputLabel>
<Select
value={selectedMonth}
onChange={onMonthChange}
label="Zeitraum"
sx={{
'& .MuiSelect-select': {
display: 'flex',
alignItems: 'center',
gap: 1
}
}}
>
{/* Months Section */}
<ListSubheader sx={{
color: 'primary.main',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: 1,
backgroundColor: 'background.paper',
lineHeight: '36px'
}}>
<CalendarIcon fontSize="small" />
Monate
</ListSubheader>
{timeRangeOptions.months.map((month) => (
<MenuItem
key={month}
value={month}
sx={{
pl: 4,
'&:hover': { backgroundColor: 'action.hover' }
}}
>
{this.getMonthName(month)}
</MenuItem>
))}
<Divider sx={{ my: 1 }} />
{/* Quarters Section */}
<ListSubheader sx={{
color: 'secondary.main',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: 1,
backgroundColor: 'background.paper',
lineHeight: '36px'
}}>
<QuarterIcon fontSize="small" />
Quartale
</ListSubheader>
{timeRangeOptions.quarters.map((quarter) => (
<MenuItem
key={quarter.value}
value={quarter.value}
sx={{
pl: 4,
color: 'secondary.main',
'&:hover': { backgroundColor: 'secondary.light', color: 'secondary.contrastText' }
}}
>
{this.getQuarterName(quarter.year, quarter.quarter)}
</MenuItem>
))}
<Divider sx={{ my: 1 }} />
{/* Years Section */}
<ListSubheader sx={{
color: 'success.main',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: 1,
backgroundColor: 'background.paper',
lineHeight: '36px'
}}>
<YearIcon fontSize="small" />
Jahre
</ListSubheader>
{timeRangeOptions.years.map((year) => (
<MenuItem
key={year.value}
value={year.value}
sx={{
pl: 4,
color: 'success.main',
'&:hover': { backgroundColor: 'success.light', color: 'success.contrastText' }
}}
>
{this.getYearName(year.year)}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Box textAlign="center">
<Typography variant="caption" color="textSecondary">Transaktionen</Typography>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#1976d2', fontSize: { xs: '0.9rem', sm: '1.25rem' } }}>
{summary.totalTransactions}
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Box textAlign="center">
<Typography variant="caption" color="textSecondary">Einnahmen</Typography>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#388e3c', fontSize: { xs: '0.9rem', sm: '1.25rem' } }}>
{this.formatAmount(summary.totalIncome)}
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Box textAlign="center">
<Typography variant="caption" color="textSecondary">Ausgaben</Typography>
<Typography variant="h6" sx={{ fontWeight: 'bold', color: '#d32f2f', fontSize: { xs: '0.9rem', sm: '1.25rem' } }}>
{this.formatAmount(summary.totalExpenses)}
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Box textAlign="center">
<Typography variant="caption" color="textSecondary">Nettobetrag</Typography>
<Typography
variant="h6"
sx={{
fontWeight: 'bold',
color: summary.netAmount >= 0 ? '#388e3c' : '#d32f2f',
fontSize: { xs: '0.9rem', sm: '1.25rem' }
}}
>
{this.formatAmount(summary.netAmount)}
</Typography>
</Box>
</Grid>
<Grid item xs={6} sm={4} md={1}>
<Box textAlign="center">
<Typography variant="caption" color="textSecondary">JTL </Typography>
<Typography
variant="h6"
sx={{
fontWeight: 'bold',
color: summary.jtlMatches === undefined ? '#856404' : '#388e3c',
fontSize: { xs: '0.9rem', sm: '1.25rem' }
}}
>
{summary.jtlMatches === undefined ? '?' : summary.jtlMatches}
</Typography>
</Box>
</Grid>
</Grid>
</Paper>
);
}
}
export default SummaryHeader;