| 25 | } |
| 26 | |
| 27 | const PerformanceChart = ({ sessions, title }: PerformanceChartProps) => { |
| 28 | const { t, i18n } = useTranslation() |
| 29 | |
| 30 | const showAxisAndLegend = useBreakpointValue({ base: false, md: true }) |
| 31 | |
| 32 | const chartData: PerformanceData[] = |
| 33 | sessions?.map((session, index) => { |
| 34 | const correctRate = |
| 35 | session.cards_practiced > 0 |
| 36 | ? Math.round((session.correct_answers / session.cards_practiced) * 100) |
| 37 | : 0 |
| 38 | const dateStr = new Date(session.created_at).toLocaleDateString(i18n.language, { |
| 39 | month: 'short', |
| 40 | day: 'numeric', |
| 41 | }) |
| 42 | return { |
| 43 | label: `#${index + 1} (${dateStr})`, |
| 44 | correctRate: correctRate, |
| 45 | cardsPracticed: session.cards_practiced, |
| 46 | } |
| 47 | }) || [] |
| 48 | |
| 49 | return ( |
| 50 | <Box p={6} borderWidth="1px" borderRadius="lg" bg="bg.50" h="100%"> |
| 51 | <Heading size="md" mb={4}> |
| 52 | {title || t('components.stats.performanceOverTime')} |
| 53 | </Heading> |
| 54 | <Box h="300px"> |
| 55 | <ResponsiveContainer width="100%" height="100%"> |
| 56 | <LineChart |
| 57 | data={chartData} |
| 58 | margin={{ |
| 59 | top: 5, |
| 60 | right: 30, |
| 61 | left: 20, |
| 62 | bottom: 5, |
| 63 | }} |
| 64 | > |
| 65 | <CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" /> |
| 66 | {showAxisAndLegend && ( |
| 67 | <XAxis |
| 68 | dataKey="label" |
| 69 | tick={{ fill: 'var(--chakra-colors-stat-axis)', fontSize: 12 }} |
| 70 | tickFormatter={(_value, index) => `#${index + 1}`} |
| 71 | /> |
| 72 | )} |
| 73 | {showAxisAndLegend && ( |
| 74 | <YAxis |
| 75 | yAxisId="left" |
| 76 | tick={{ fill: 'var(--chakra-colors-stat-axis)' }} |
| 77 | domain={[0, 100]} |
| 78 | tickFormatter={(value) => `${value}%`} |
| 79 | /> |
| 80 | )} |
| 81 | {showAxisAndLegend && ( |
| 82 | <YAxis |
| 83 | yAxisId="right" |
| 84 | orientation="right" |