Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x | /**
* @file ItemUpdateFrequencyCard.tsx
* @module pages/analytics/blocks/ItemUpdateFrequencyCard
*
* @summary
* Most-updated items for a supplier (top N). If no supplier is selected, shows an empty-state hint.
*
* @enterprise
* - Hook order is stable: useQuery is declared unconditionally and gated via `enabled`.
* - Bars are individually colored via <Cell>, cycling through MUI theme palette.
* - Layout uses vertical bars; right margin avoids clipping on narrow screens.
*/
import { Card, CardContent, Typography, Skeleton, Box } from '@mui/material';
import { useTheme as useMuiTheme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar, Cell } from 'recharts';
import { getItemUpdateFrequency, type ItemUpdateFrequencyPoint } from '../../../api/analytics/frequency';
import { useSettings } from '../../../hooks/useSettings';
import { formatNumber } from '../../../utils/formatters';
export type ItemUpdateFrequencyCardProps = { supplierId?: string | null };
export default function ItemUpdateFrequencyCard({ supplierId }: ItemUpdateFrequencyCardProps) {
const { t } = useTranslation(['analytics']);
const muiTheme = useMuiTheme();
const { userPreferences } = useSettings();
// Fetch (unconditional hook; gated by `enabled`)
const enabled = !!supplierId;
const q = useQuery<ItemUpdateFrequencyPoint[]>({
queryKey: ['analytics', 'itemUpdateFrequency', supplierId ?? null],
queryFn: () => getItemUpdateFrequency(supplierId ?? ''),
enabled,
});
// Empty-state when supplier is not chosen
if (!enabled) {
return (
<Card>
<CardContent>
<Typography variant="subtitle1" sx={{ mb: 1 }}>
{t('analytics:frequency.title', 'Top updated items')}
</Typography>
<Box sx={{ color: 'text.secondary' }}>
{t('analytics:frequency.selectSupplier', 'Select a supplier to view')}
</Box>
</CardContent>
</Card>
);
}
// Stable data + color cycle
const data: ItemUpdateFrequencyPoint[] = q.data ?? [];
const barColors = [
muiTheme.palette.primary.main,
muiTheme.palette.success.main,
muiTheme.palette.info.main,
muiTheme.palette.warning.main,
muiTheme.palette.error.main,
];
return (
<Card>
<CardContent>
<Typography variant="subtitle1" sx={{ mb: 1 }}>
{t('analytics:frequency.title', 'Top updated items')}
</Typography>
{q.isLoading ? (
<Skeleton variant="rounded" height={220} />
) : (
<Box sx={{ height: 260 }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data} layout="vertical" margin={{ left: 24, right: 16 }}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis
type="number"
tickFormatter={(value) => formatNumber(Number(value), userPreferences.numberFormat, 0)}
/>
<YAxis type="category" dataKey="name" width={140} />
<Tooltip
formatter={(value: number | string) =>
typeof value === 'number'
? formatNumber(value, userPreferences.numberFormat, 0)
: value
}
/>
<Bar dataKey="updates" isAnimationActive={false}>
{data.map((_, i) => (
<Cell key={`u-${i}`} fill={barColors[i % barColors.length]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</Box>
)}
</CardContent>
</Card>
);
}
|