All files / src/pages/analytics/blocks MovementLineCard.tsx

0% Statements 0/106
0% Branches 0/1
0% Functions 0/1
0% Lines 0/106

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 103 104 105 106 107                                                                                                                                                                                                                     
/**
 * @file MovementLineCard.tsx
 * @module pages/analytics/blocks/MovementLineCard
 *
 * @summary
 * Line chart version of monthly stock movement (A2) with two series:
 * Stock In vs Stock Out over months in the selected date range.
 *
 * @enterprise
 * - Reuses the same API as the bar version; purely a presentation swap.
 * - Supplier-aware via props; hook is unconditional and keyed by filters.
 */

import * as React from 'react';
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, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line } from 'recharts';
import { getMonthlyStockMovement, type MonthlyMovement } from '../../../api/analytics';
import { useSettings } from '../../../hooks/useSettings';
import { formatDate, formatNumber } from '../../../utils/formatters';

export type MovementLineCardProps = { from?: string; to?: string; supplierId?: string | null };

export default function MovementLineCard({ from, to, supplierId }: MovementLineCardProps) {
  const { t } = useTranslation(['analytics']);
  const muiTheme = useMuiTheme();
  const { userPreferences } = useSettings();

  const q = useQuery<MonthlyMovement[]>({
    queryKey: ['analytics', 'movementLine', from ?? null, to ?? null, supplierId ?? null],
    queryFn: () => getMonthlyStockMovement({ from, to, supplierId: supplierId ?? undefined }),
    staleTime: 60_000,
  });

  const data = React.useMemo(
    () => [...(q.data ?? [])],
    [q.data]
  );
  const formatDateLabel = React.useCallback(
    (value: string | number) => {
      const str = String(value);
      const formatted = formatDate(str, userPreferences.dateFormat);
      return formatted || str;
    },
    [userPreferences.dateFormat]
  );

  return (
    <Card>
      <CardContent>
        <Typography variant="subtitle1" sx={{ mb: 1 }}>
          {t('analytics:cards.monthlyMovement')}
        </Typography>

        {q.isLoading ? (
          <Skeleton variant="rounded" height={220} />
        ) : data.length === 0 ? (
          <Box sx={{ height: 220, display: 'grid', placeItems: 'center', color: 'text.secondary' }}>
            {t('analytics:cards.noData')}
          </Box>
        ) : (
          <Box sx={{ height: 260 }}>
            <ResponsiveContainer width="100%" height="100%">
              <LineChart data={data} margin={{ top: 8, right: 16, left: 8, bottom: 8 }}>
                <CartesianGrid strokeDasharray="3 3" />
                <XAxis dataKey="month" tickFormatter={formatDateLabel} />
                <YAxis tickFormatter={(value) => formatNumber(Number(value), userPreferences.numberFormat, 0)} />
                <Tooltip
                  labelFormatter={(value) => formatDateLabel(value as string)}
                  formatter={(value: number | string) =>
                    typeof value === 'number'
                      ? formatNumber(value, userPreferences.numberFormat, 0)
                      : value
                  }
                />
                <Legend />
                <Line
                  type="monotone"
                  dataKey="stockIn"
                  name={t('analytics:cards.monthlyMovement') + ' • In'}
                  stroke={muiTheme.palette.success.main}
                  strokeWidth={2}
                  dot={{ r: 2 }}
                  activeDot={{ r: 4 }}
                  isAnimationActive={false}
                />
                <Line
                  type="monotone"
                  dataKey="stockOut"
                  name={t('analytics:cards.monthlyMovement') + ' • Out'}
                  stroke={muiTheme.palette.error.main}
                  strokeWidth={2}
                  dot={{ r: 2 }}
                  activeDot={{ r: 4 }}
                  isAnimationActive={false}
                />
              </LineChart>
            </ResponsiveContainer>
          </Box>
        )}
      </CardContent>
    </Card>
  );
}