diff --git a/package-lock.json b/package-lock.json
index 4f419e7..4a6cb79 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27,6 +27,7 @@
"react-scripts": "5.0.1",
"react-switch": "^7.0.0",
"react-youtube": "^10.1.0",
+ "recharts": "^2.13.3",
"smooth-scroll-into-view-if-needed": "^2.0.2",
"socket.io-client": "^4.7.5",
"styled-components": "^6.1.11",
@@ -4648,6 +4649,11 @@
"@types/node": "*"
}
},
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
+ },
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
@@ -4658,6 +4664,11 @@
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="
},
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
+ },
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
@@ -4692,6 +4703,11 @@
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
},
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
+ },
"node_modules/@types/eslint": {
"version": "8.56.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
@@ -7534,6 +7550,14 @@
"node": ">=12"
}
},
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
@@ -7609,6 +7633,14 @@
"node": ">=12"
}
},
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@@ -7696,6 +7728,11 @@
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
},
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
+ },
"node_modules/dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
@@ -9201,6 +9238,14 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
+ "node_modules/fast-equals": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz",
+ "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@@ -16199,6 +16244,20 @@
"node": ">=10"
}
},
+ "node_modules/react-smooth": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz",
+ "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==",
+ "dependencies": {
+ "fast-equals": "^5.0.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-switch": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/react-switch/-/react-switch-7.0.0.tgz",
@@ -16274,6 +16333,41 @@
"node": ">=8.10.0"
}
},
+ "node_modules/recharts": {
+ "version": "2.13.3",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.3.tgz",
+ "integrity": "sha512-YDZ9dOfK9t3ycwxgKbrnDlRC4BHdjlY73fet3a0C1+qGMjXVZe6+VXmpOIIhzkje5MMEL8AN4hLIe4AMskBzlA==",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.21",
+ "react-is": "^18.3.1",
+ "react-smooth": "^4.0.0",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/recharts/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
"node_modules/recursive-readdir": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
@@ -18136,6 +18230,11 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
},
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -18640,6 +18739,27 @@
"node": ">= 0.8"
}
},
+ "node_modules/victory-vendor": {
+ "version": "36.9.2",
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ },
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
diff --git a/package.json b/package.json
index 8eea11c..41af52d 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"react-scripts": "5.0.1",
"react-switch": "^7.0.0",
"react-youtube": "^10.1.0",
+ "recharts": "^2.13.3",
"smooth-scroll-into-view-if-needed": "^2.0.2",
"socket.io-client": "^4.7.5",
"styled-components": "^6.1.11",
diff --git a/src/components/BarChart.js b/src/components/BarChart.js
new file mode 100644
index 0000000..86ec90e
--- /dev/null
+++ b/src/components/BarChart.js
@@ -0,0 +1,178 @@
+import React, { useRef, useEffect } from 'react';
+import { BarChart, Bar, XAxis, Tooltip, Legend, CartesianGrid, ResponsiveContainer, LabelList } from 'recharts';
+
+// Colors palette
+const colors = [
+ // **Analogous Colors** (near greens, yellows)
+ "#D0E14F", // Light green-yellow
+ "#B2B83D", // Olive yellow
+ "#A9C96E", // Muted olive green (your bg color itself)
+ "#A8B64E", // Olive green with yellow undertones
+
+ // **Complementary Colors** (contrast with green)
+ "#FF6347", // Tomato red
+ "#FF4500", // Orange red
+ "#FF8C00", // Dark orange
+ "#FF7F50", // Coral
+
+ // **Triadic Colors** (balanced vibrant combination)
+ "#1E90FF", // Dodger blue
+ "#FF00FF", // Magenta
+ "#32CD32", // Lime green
+
+ // **Tetradic Colors** (4-color palette: 2 complementary pairs)
+ "#00CED1", // Dark turquoise
+ "#FFD700", // Gold
+ "#FF1493", // Deep pink
+ "#8A2BE2", // Blue violet
+
+ // **Neutral Tones** (earthy, muted tones to balance the palette)
+ "#FFDAB9", // Peach
+ "#F0E68C", // Khaki
+ "#8B4513", // Saddle brown
+ "#4B0082", // Indigo
+ "#C71585", // Medium violet red
+
+ // **Pastels for softer contrast**
+ "#FFB6C1", // Light pink
+ "#E6E6FA", // Lavender
+ "#98FB98", // Pale green
+ "#F0FFF0", // Honeydew
+ "#D3D3D3", // Light grey
+ ];
+
+
+const SimpleBarChart = ({ Data }) => {
+ const lastMonthRef = useRef('');
+ const lastYearRef = useRef('');
+ const usedColors = useRef([]); // Keep track of used colors
+
+ useEffect(() => {
+ usedColors.current = [];
+ }, [Data]);
+
+ // Ensure Data is not null or undefined
+ const transformedData = (Data?.length ? Data.reduce((acc, { date, materialId, priceAtp, stockDifference, name }) => {
+ function isSameDay(date1, date2) {
+ const d1 = new Date(date1);
+ const d2 = new Date(date2);
+
+ return d1.getFullYear() === d2.getFullYear() &&
+ d1.getMonth() === d2.getMonth() &&
+ d1.getDate() === d2.getDate();
+ }
+
+ // Assuming `acc` is an array of entries and `date` is the input date you're checking
+ const existingEntry = acc.find(d => isSameDay(d.date, date));
+
+ console.log(existingEntry)
+ if (existingEntry) {
+ // If it exists, sum the stockDifference (priceAtp * stockDifference)
+ existingEntry[name] = (existingEntry[name] || 0) + (priceAtp * stockDifference);
+ } else {
+ // If it doesn't exist yet, add a new entry
+ acc.push({
+ date,
+ materialId,
+ name,
+ [name]: priceAtp * stockDifference
+ });
+ }
+
+ return acc;
+ }, []) : []);
+
+ // Sort the data by date (ascending) to easily find the oldest date
+ const sortedData = [...transformedData].sort((a, b) => new Date(a.date) - new Date(b.date));
+
+ // The first date in the sorted array will be the oldest
+ const oldestDate = sortedData[0]?.date;
+
+ // Get the unique materials (names) from the data
+ const uniqueMaterials = Array.from(new Set(Data?.map(item => item.name) || []));
+
+ // Function to format date and handle month and year changes
+ const formatDate = (date) => {
+ const formattedDate = new Date(date);
+ const day = formattedDate.getDate(); // Get the day of the month
+ const month = formattedDate.toLocaleString('en-US', { month: 'short' }); // Get the abbreviated month (e.g., "Nov")
+ const year = formattedDate.getFullYear().toString().slice(2); // Get the last two digits of the year (e.g., "24")
+
+ // Extract the month and year from the date
+ const currentMonth = formattedDate.toLocaleDateString('en-US', { month: 'short' });
+ const currentYear = formattedDate.getFullYear();
+
+ // Check if it's the oldest date (first entry)
+ if (date === oldestDate) {
+ lastMonthRef.current = currentMonth; // Update lastMonthRef for the first entry
+ lastYearRef.current = currentYear; // Update lastYearRef for the first entry
+ return `${day} ${month} ${year}`; // Format as "day month year" (e.g., "4 Nov 24")
+ }
+
+ // If the year changes, show the full date with year
+ if (currentYear !== lastYearRef.current) {
+ lastYearRef.current = currentYear; // Update year reference
+ return `${day} ${month} ${year}`; // Show full date: day month year
+ }
+
+ // If the month changes, show the full date with month and year
+ if (currentMonth !== lastMonthRef.current) {
+ lastMonthRef.current = currentMonth; // Update month reference
+ return `${day} ${month} ${year}`; // Show full date: day month year
+ }
+
+ // Only show the day if the month hasn't changed
+ return `${day}`; // Show just the day if the month remains the same
+ };
+
+ // Function to get the next available color from the palette, starting from a random index
+ const getNextColor = () => {
+ // Randomly pick a starting index from the color palette
+ const randomIndex = Math.floor(Math.random() * colors.length);
+
+ // Find the first unused color from the random starting point
+ let color = null;
+ for (let i = 0; i < colors.length; i++) {
+ const index = (randomIndex + i) % colors.length;
+ if (!usedColors.current.includes(colors[index])) {
+ color = colors[index];
+ usedColors.current.push(color); // Mark color as used
+ break;
+ }
+ }
+
+ return color || '#000000'; // Fallback color if no colors are available
+ };
+
+ // Extract unique dates for the XAxis
+ const uniqueDates = Array.from(new Set(transformedData.map(item => item.date)));
+
+ return (
+
+
+
+ {/* Format the XAxis ticks to show only the month when it changes */}
+
+
+
+
+
+ {/* Dynamically create bars and labels for each unique material */}
+ {uniqueMaterials.map((material) => (
+
+
+
+
+ ))}
+
+
+