вот мой код App.tx import './scss/app.scss';
import React, { useState } from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import ExcelUploader from './components/ExcelUploader';
import Sidebar from './components/Sidebar';
import DetailsPage from './components/DetailsPage';

interface ArtikulMap {
  [artikul: string]: string;
}

interface DataRow {
  [key: string]: string | number;
}

const App: React.FC = () => {
  const [artikulMap, setArtikulMap] = useState<ArtikulMap>({});
  const [selectedItem, setSelectedItem] = useState<string | null>(null);
  const [data, setData] = useState<DataRow[]>([]);
  const [isDataLoaded, setIsDataLoaded] = useState<boolean>(false);

  const handleArtikulUpdate = (newArtikulMap: ArtikulMap, newData: DataRow[]) => {
    setArtikulMap(newArtikulMap);
    setData(newData);
    setIsDataLoaded(Object.keys(newArtikulMap).length > 0);
  };

  return (
    <Router>
      <div className="app-container">
        <Sidebar
          artikuls={artikulMap}
          onSelectItem={setSelectedItem}
          isDataLoaded={isDataLoaded}
          data={data} // Добавляем data
        />
        <Routes>
          <Route path="/upload" element={<ExcelUploader onArtikulUpdate={handleArtikulUpdate} />} />
          <Route
            path="/details"
            element={
              <DetailsPage artikulMap={artikulMap} selectedItem={selectedItem} data={data} />
            }
          />
          <Route path="*" element={<Navigate to="/upload" />} />
        </Routes>
      </div>
    </Router>
  );
};

export default App;
DetailsPage.tsx import React from 'react';
import { useNavigate } from 'react-router-dom';
import { PieChart, Pie, Cell, Tooltip } from 'recharts';
import styles from './DetailsPage.module.scss';

interface DataRow {
  [key: string]: string | number;
}

interface DetailsPageProps {
  artikulMap: { [key: string]: string };
  selectedItem: string | null;
  data: DataRow[];
}

interface CustomTooltipProps {
  active?: boolean;
  payload?: { name: string; value: number }[]; // Подправьте этот объект на основе структуры данных.
  label?: any;
  total: number;
}

const DetailsPage: React.FC<DetailsPageProps> = ({ artikulMap, selectedItem, data }) => {
  const navigate = useNavigate();

  const additionalColumns = [
    'Хранение',
    'Удержания',
    'Платная приемка',
    'Услуги по доставке товара покупателю',
    'Общая сумма штрафов',
  ];

  const calculateTotalForKey = (key: string, onlySales: boolean = false): number => {
    const total = data.reduce((sum: number, item: DataRow) => {
      const isSelectedItem = selectedItem === null || item['Артикул поставщика'] === selectedItem;
      const isSaleDocument = !onlySales || item['Тип документа'] === 'Продажа';

      if (isSelectedItem && isSaleDocument) {
        const value = Number(item[key]);
        if (!isNaN(value)) {
          return sum + value;
        }
      }
      return sum;
    }, 0);
    return total;
  };

  const calculateTotalSold = (): number => {
    return calculateTotalForKey('Кол-во', true);
  };

  const calculateTotalForPayout = (): number => {
    return calculateTotalForKey('К перечислению Продавцу за реализованный Товар', true);
  };

  const calculateAdditionalServicesTotal = (): number => {
    return additionalColumns.reduce((total: number, colName: string) => {
      return total + calculateTotalForKey(colName);
    }, 0);
  };

  const calculatePayout = (): string => {
    const totalToSeller = calculateTotalForPayout();
    const additionalServicesTotal = calculateAdditionalServicesTotal();
    const payout = totalToSeller - additionalServicesTotal;
    return payout.toFixed(2);
  };

  const renameColName = (colName: string) => {
    if (colName === 'Услуги по доставке товара покупателю') {
      return 'Логистика';
    }
    return colName;
  };

  const handleUploadNavigation = () => {
    navigate('/upload');
  };

  // Prepare data for Pie Charts
  const artikulData = Object.keys(artikulMap)
    .map((artikul) => {
      const sum = data.reduce((acc, item) => {
        if (item['Артикул поставщика'] === artikul) {
          const value = Number(item['К перечислению Продавцу за реализованный Товар']);
          return acc + (isNaN(value) ? 0 : value);
        }
        return acc;
      }, 0);
      return { name: artikulMap[artikul], value: parseFloat(sum.toFixed(2)) };
    })
    .sort((a, b) => a.name.localeCompare(b.name));

  const skladMap: { [key: string]: number } = {};
  data.forEach((row) => {
    const sklad = row['Склад'] as string;
    const value = Number(row['Кол-во']);
    if (!isNaN(value) && sklad !== '') {
      if (skladMap[sklad]) {
        skladMap[sklad] += value;
      } else {
        skladMap[sklad] = value;
      }
    }
  });

  const skladData = Object.entries(skladMap).map(([name, value]) => ({
    name,
    value,
  }));

  console.log(skladMap);

  function generateDistinctColors(numColors: number): string[] {
    const colorSet = new Set<string>();
    while (colorSet.size < numColors) {
      const randomColor = `#${Math.floor(Math.random() * 16777215)
        .toString(16)
        .padStart(6, '0')}`;
      colorSet.add(randomColor);
    }
    return Array.from(colorSet);
  }

  const COLORS = generateDistinctColors(1000);

  const totalPayout = calculateTotalForPayout();

  const CustomTooltip: React.FC<CustomTooltipProps> = ({ active, payload, label, total }) => {
    if (active && payload && payload.length) {
      const { name, value } = payload[0];
      const percentage = ((value / total) * 100).toFixed(2); // Рассчитываем процент
      return (
        <div className={styles.customTooltip}>
          <p>{`${name} : ${value.toFixed(2)} руб.`}</p>
          <p>{`Процент от общей продажи: ${percentage}%`}</p>
        </div>
      );
    }
    return null;
  };

  return (
    <div className={styles.content}>
      <button onClick={handleUploadNavigation} className={styles.backButton}>
        <svg
          version="1.1"
          id="Layer_1"
          xmlns="http://www.w3.org/2000/svg"
          width="18px"
          height="18px"
          fill="#ffffff"
          viewBox="0 0 122.883 122.882"
          enableBackground="new 0 0 122.883 122.882">
          <g>
            <path d="M61.441,0c16.967,0,32.327,6.877,43.446,17.996c11.119,11.119,17.996,26.479,17.996,43.445 c0,16.966-6.877,32.326-17.996,43.445c-11.119,11.118-26.479,17.995-43.446,17.995c-16.966,0-32.326-6.877-43.445-17.995 C6.877,93.768,0,78.407,0,61.441c0-16.967,6.877-32.327,17.996-43.445C29.115,6.877,44.475,0,61.441,0L61.441,0z M71.377,42.166 c1.736-1.784,1.695-4.637-0.088-6.372s-4.637-1.696-6.373,0.088L43.078,58.402l3.23,3.142l-3.244-3.146 c-1.737,1.792-1.693,4.652,0.099,6.39c0.052,0.05,0.104,0.099,0.158,0.146l21.595,22.082c1.736,1.784,4.59,1.823,6.373,0.088 c1.783-1.734,1.824-4.588,0.088-6.372L52.598,61.531L71.377,42.166L71.377,42.166z M98.496,24.386 C89.014,14.903,75.912,9.038,61.441,9.038s-27.572,5.865-37.055,15.348C14.903,33.869,9.038,46.97,9.038,61.441 c0,14.471,5.865,27.572,15.349,37.055c9.482,9.483,22.583,15.349,37.055,15.349s27.573-5.865,37.055-15.349 c9.484-9.482,15.35-22.584,15.35-37.055C113.846,46.97,107.98,33.869,98.496,24.386L98.496,24.386z" />
          </g>
        </svg>

        <p>Загрузить новый файл</p>
      </button>
      <div className={styles.wrapper}>
        {selectedItem ? (
          <div className={styles.articlesTotalSum}>
            <h3 className={styles.articleTitle}> {artikulMap[selectedItem]}</h3>
            <div className={styles.totalSumWrapper}>
              <p className={styles.ItemTitle}>Общая сумма "К перечислению": </p>
              <p className={styles.ItemNumber}>{calculateTotalForPayout().toFixed(2)} руб.</p>
              <p className={styles.ItemTitle}>Продано:</p>
              <p className={styles.ItemNumber}>{calculateTotalSold()} шт.</p>
              <p className={styles.ItemTitle}>К выплате:</p>
              <p className={styles.ItemNumber}>{calculatePayout()} руб.</p>
            </div>
          </div>
        ) : (
          <div className={styles.articlesTotalSum}>
            <h3 className={styles.articleTitle}>Общие показатели</h3>
            <div className={styles.totalSumWrapper}>
              <p className={styles.ItemTitle}>Общая сумма "К перечислению": </p>
              <p className={styles.ItemNumber}>{calculateTotalForPayout().toFixed(2)} руб.</p>
              <p className={styles.ItemTitle}>Продано:</p>
              <p className={styles.ItemNumber}>{calculateTotalSold()} шт.</p>
              <p className={styles.ItemTitle}>К выплате:</p>
              <p className={styles.ItemNumber}>{calculatePayout()} руб.</p>
            </div>
          </div>
        )}

        <div className={styles.expenses}>
          <h3 className={styles.expensesTitle}>Общая сумма дополнительных услуг</h3>
          <ul className={styles.expensesList}>
            {additionalColumns.map((colName: string) => (
              <li className={styles.expensesItem} key={colName}>
                <p className={styles.ItemTitle}>{renameColName(colName)}:</p>
                <p className={styles.ItemNumber}>{calculateTotalForKey(colName).toFixed(2)} руб.</p>
              </li>
            ))}
          </ul>
        </div>

        {/* Pie Chart for Artikuls */}
      </div>

      <div className={styles.chartWrapper}>
        <div className={styles.chartContainer}>
          <h3 className={styles.chartTitle}>Процентное соотношение по артикулам</h3>
          <div className={styles.chartContent}>
            {/* <ul className={styles.legend}>
              {artikulData.map((entry, index) => (
                <li key={`item-${index}`}>
                  <span
                    className={styles.legendColor}
                    style={{ backgroundColor: COLORS[index % COLORS.length] }}
                  />
                  {entry.name}
                </li>
              ))}
            </ul> */}
            <PieChart width={300} height={300}>
              <Pie
                data={artikulData}
                cx={150}
                cy={150}
                labelLine={false}
                outerRadius={130}
                fill="#8884d8"
                dataKey="value">
                {artikulData.map((entry, index) => (
                  <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
                ))}
              </Pie>
              <Tooltip content={<CustomTooltip total={totalPayout} />} />
            </PieChart>
          </div>
        </div>

        {/* Pie Chart for Sklady */}
        <div className={styles.chartContainer}>
          <h3 className={styles.chartTitle}>Процентное соотношение по складам</h3>
          <div className={styles.chartContent}>
            {/* <ul className={styles.legend}>
              {skladData.map((entry, index) => (
                <li key={`item-${index}`}>
                  <span
                    className={styles.legendColor}
                    style={{ backgroundColor: COLORS[index % COLORS.length] }}
                  />
                  {entry.name}
                </li>
              ))}
            </ul> */}
            <PieChart width={300} height={300}>
              <Pie
                data={skladData}
                cx={150}
                cy={150}
                labelLine={false}
                outerRadius={130}
                fill="#8884d8"
                dataKey="value">
                {skladData.map((entry, index) => (
                  <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
                ))}
              </Pie>
              <Tooltip />
            </PieChart>
          </div>
        </div>
      </div>
    </div>
  );
};

export default DetailsPage;
Sidebar.tsx import React, { useState, useEffect } from 'react';
import styles from './Sidebar.module.scss';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import {
  Accordion,
  AccordionItem,
  AccordionItemHeading,
  AccordionItemButton,
  AccordionItemPanel,
} from 'react-accessible-accordion';

interface Article {
  [key: string]: string;
}

interface DataRow {
  [key: string]: string | number;
}

interface Categories {
  [key: string]: Article;
}

interface SidebarProps {
  artikuls: Article;
  onSelectItem: (item: string | null) => void;
  isDataLoaded: boolean;
  data: DataRow[];
}

const Sidebar: React.FC<SidebarProps> = ({ artikuls, onSelectItem, isDataLoaded, data }) => {
  const [categories, setCategories] = useState<Categories>({});

  useEffect(() => {
    const computeCategories = (): Categories => {
      const newCategories: Categories = {};

      for (const row of data) {
        const artikul = row['Артикул поставщика'] as string;
        const name = artikuls[artikul];
        const category = row['Предмет'] as string;

        if (!name || !category) continue;

        if (!newCategories[category]) {
          newCategories[category] = {};
        }
        newCategories[category][artikul] = name;
      }

      return newCategories;
    };

    setCategories(computeCategories());
  }, [data, artikuls]);

  return (
    <div className={styles.sidebar}>
      <h2 className={styles.logo}>Excelmate</h2>
      <hr />
      <h3 className={styles.sidebarTitle}>Наименование товара</h3>
      {isDataLoaded && (
        <div>
          <ul className={styles.sidebarList}>
            <li onClick={() => onSelectItem(null)}>Все артикулы</li>
          </ul>
          <Accordion allowZeroExpanded>
            {Object.entries(categories).map(([category, items]) => (
              <AccordionItem key={category}>
                <AccordionItemHeading>
                  <AccordionItemButton>{category}</AccordionItemButton>
                </AccordionItemHeading>
                <AccordionItemPanel>
                  <SimpleBar style={{ maxHeight: '300px', overflowX: 'hidden' }}>
                    <ul className={styles.sidebarList}>
                      {Object.entries(items).map(([artikul, name]) => (
                        <li key={artikul} onClick={() => onSelectItem(artikul)}>
                          {name} ({artikul})
                        </li>
                      ))}
                    </ul>
                  </SimpleBar>
                </AccordionItemPanel>
              </AccordionItem>
            ))}
          </Accordion>
        </div>
      )}
    </div>
  );
};

export default React.memo(Sidebar); нужно при нажатии на кнопку <button onClick={handleUploadNavigation} className={styles.backButton}> чтобы очищался const [categories, setCategories] = useState<Categories>({});