first commit

This commit is contained in:
2025-03-25 11:47:41 +05:30
commit 61b3c4ee2c
211 changed files with 36430 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import React from "react";
import RenameInput from "./inputs/RenameInput";
const FileMenu: React.FC = () => {
return (
<div className="project-dropdowm-container">
<div className="project-name">
<RenameInput value="untitled" />
</div>
</div>
);
};
export default FileMenu;

View File

@@ -0,0 +1,59 @@
import React from "react";
import useModuleStore from "../../store/useModuleStore";
import {
BuilderIcon,
CartIcon,
SimulationIcon,
VisualizationIcon,
} from "../icons/ExportModuleIcons";
const ModuleToggle: React.FC = () => {
const { activeModule, setActiveModule } = useModuleStore();
return (
<div className="module-toggle-container">
<div
className={`module-list ${activeModule === "builder" && "active"}`}
onClick={() => setActiveModule("builder")}
>
<div className="icon">
<BuilderIcon isActive={activeModule === "builder"} />
</div>
<div className="module">Builder</div>
</div>
<div
className={`module-list ${activeModule === "simulation" && "active"}`}
onClick={() => setActiveModule("simulation")}
>
<div className="icon">
<SimulationIcon isActive={activeModule === "simulation"} />
</div>
<div className="module">Simulation</div>
</div>
<div
className={`module-list ${
activeModule === "visualization" && "active"
}`}
onClick={() => setActiveModule("visualization")}
>
<div className="icon">
<VisualizationIcon isActive={activeModule === "visualization"} />
</div>
<div className="module">Visualization</div>
</div>
<div
className={`module-list ${
activeModule === "market" && "active"
}`}
onClick={() => setActiveModule("market")}
>
<div className="icon">
<CartIcon isActive={activeModule === "market"} />
</div>
<div className="module">Market Place</div>
</div>
</div>
);
};
export default ModuleToggle;

View File

@@ -0,0 +1,251 @@
import React, { useEffect, useRef, useState } from "react";
import {
AsileIcon,
CommentIcon,
CursorIcon,
FloorIcon,
FreeMoveIcon,
PenIcon,
PlayIcon,
SaveTemplateIcon,
WallIcon,
ZoneIcon,
} from "../icons/ExportToolsIcons";
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
import useModuleStore from "../../store/useModuleStore";
import { handleSaveTemplate } from "../../modules/visualization/handleSaveTemplate";
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/useZoneStore";
const Tools: React.FC = () => {
const { templates } = useTemplateStore();
const [activeTool, setActiveTool] = useState("cursor");
const [activeSubTool, setActiveSubTool] = useState("cursor");
const [toggleThreeD, setToggleThreeD] = useState(true);
const dropdownRef = useRef<HTMLDivElement>(null);
const [openDrop, setOpenDrop] = useState(false);
const { activeModule } = useModuleStore();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { addTemplate } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore();
// Reset activeTool whenever activeModule changes
useEffect(() => {
setActiveTool(activeSubTool);
setActiveSubTool(activeSubTool);
}, [activeModule]);
useEffect(() => {
const handleOutsideClick = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setOpenDrop(false); // Close the dropdown
}
};
document.addEventListener("mousedown", handleOutsideClick);
return () => {
document.removeEventListener("mousedown", handleOutsideClick);
};
}, []);
return (
<div className="tools-container">
<div className="drop-down-icons">
<div className="activeDropicon">
{activeSubTool == "cursor" && (
<div
className={`tool-button ${
activeTool === "cursor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("cursor");
}}
>
<CursorIcon isActive={activeTool === "cursor"} />
</div>
)}
{activeSubTool == "free-hand" && (
<div
className={`tool-button ${
activeTool === "free-hand" ? "active" : ""
}`}
onClick={() => {
setActiveTool("free-hand");
}}
>
<FreeMoveIcon isActive={activeTool === "free-hand"} />
</div>
)}
{activeModule !== "visualization" && (
<div
className="drop-down-option-button"
ref={dropdownRef}
onClick={() => {
setOpenDrop(!openDrop);
console.log(openDrop);
}}
>
<ArrowIcon />
{openDrop && (
<div className="drop-down-container">
<div
className="option-list"
onClick={() => {
setOpenDrop(false);
setActiveTool("cursor");
setActiveSubTool("cursor");
}}
>
<div className="active-option">
{activeSubTool === "cursor" && <TickIcon />}
</div>
<CursorIcon isActive={false} />
<div className="option">Cursor</div>
</div>
<div
className="option-list"
onClick={() => {
setOpenDrop(false);
setActiveTool("free-hand");
setActiveSubTool("free-hand");
}}
>
<div className="active-option">
{activeSubTool === "free-hand" && <TickIcon />}
</div>
<FreeMoveIcon isActive={false} />
<div className="option">Free Hand</div>
</div>
</div>
)}
</div>
)}
</div>
</div>
{!toggleThreeD && activeModule === "builder" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${
activeTool === "draw-wall" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-wall");
}}
>
<WallIcon isActive={activeTool === "draw-wall"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-zone" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-zone");
}}
>
<ZoneIcon isActive={activeTool === "draw-zone"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-aisle" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-aisle");
}}
>
<AsileIcon isActive={activeTool === "draw-aisle"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-floor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-floor");
}}
>
<FloorIcon isActive={activeTool === "draw-floor"} />
</div>
</div>
</>
)}
{activeModule === "simulation" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${activeTool === "pen" ? "active" : ""}`}
onClick={() => {
setActiveTool("pen");
}}
>
<PenIcon isActive={activeTool === "pen"} />
</div>
</div>
</>
)}
{activeModule === "visualization" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button`}
onClick={() =>
handleSaveTemplate({
addTemplate,
selectedZone,
templates,
})
}
>
<SaveTemplateIcon isActive={false} />
</div>
</div>
</>
)}
<div className="split"></div>
<div className="general-options">
<div
className={`tool-button ${activeTool === "comment" ? "active" : ""}`}
onClick={() => {
setActiveTool("comment");
}}
>
<CommentIcon isActive={activeTool === "comment"} />
</div>
<div
className={`tool-button ${activeTool === "play" ? "active" : ""}`}
onClick={() => {
setActiveTool("play");
setIsPlaying(!isPlaying);
}}
>
<PlayIcon isActive={activeTool === "play"} />
</div>
</div>
<div className="split"></div>
<div
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""}`}
onClick={() => {
setToggleThreeD(!toggleThreeD);
}}
>
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}>
2d
</div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}>
3d
</div>
</div>
</div>
);
};
export default Tools;

View File

@@ -0,0 +1,94 @@
import { useMemo } from "react";
import { Bar } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const LineGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1",
borderColor: "#ffffff",
borderWidth: 2,
fill: false,
},
],
};
return <Bar data={chartData} options={options} />;
};
export default LineGraphComponent;

View File

@@ -0,0 +1,93 @@
import { useMemo } from "react";
import { Line } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const LineGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
};
return <Line data={chartData} options={options} />;
};
export default LineGraphComponent;

View File

@@ -0,0 +1,91 @@
import { useMemo } from "react";
import { Pie } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const PieChartComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
// Access the CSS variable for the primary accent color
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue("--accent-color")
.trim();
console.log("accentColor: ", accentColor);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#ffffff",
borderWidth: 2,
},
],
};
return <Pie data={chartData} options={options} />;
};
export default PieChartComponent;

View File

@@ -0,0 +1,192 @@
import React from "react";
import {
CleanPannel,
EyeIcon,
LockIcon,
} from "../../icons/RealTimeVisulationIcons";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
// Define the type for the props passed to the Buttons component
interface ButtonsProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
}>
>;
hiddenPanels: Side[]; // Add this prop for hidden panels
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
}
const AddButtons: React.FC<ButtonsProps> = ({
selectedZone,
setSelectedZone,
setHiddenPanels,
hiddenPanels,
}) => {
// Local state to track hidden panels
// Function to toggle lock/unlock a panel
const toggleLockPanel = (side: Side) => {
const newLockedPanels = selectedZone.lockedPanels.includes(side)
? selectedZone.lockedPanels.filter((panel) => panel !== side)
: [...selectedZone.lockedPanels, side];
const updatedZone = {
...selectedZone,
lockedPanels: newLockedPanels,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
};
// Function to toggle visibility of a panel
const toggleVisibility = (side: Side) => {
const isHidden = hiddenPanels.includes(side);
if (isHidden) {
// If the panel is already hidden, remove it from the hiddenPanels array
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
} else {
// If the panel is visible, add it to the hiddenPanels array
setHiddenPanels([...hiddenPanels, side]);
}
};
// Function to clean all widgets from a panel
const cleanPanel = (side: Side) => {
const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side
);
const updatedZone = {
...selectedZone,
widgets: cleanedWidgets,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
};
// Function to handle "+" button click
const handlePlusButtonClick = (side: Side) => {
if (selectedZone.activeSides.includes(side)) {
// If the panel is already active, remove all widgets and close the panel
const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side
);
const newActiveSides = selectedZone.activeSides.filter((s) => s !== side);
const updatedZone = {
...selectedZone,
widgets: cleanedWidgets,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
} else {
// If the panel is not active, activate it
const newActiveSides = [...selectedZone.activeSides, side];
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
}
};
return (
<div>
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
<div key={side} className={`side-button-container ${side}`}>
{/* "+" Button */}
<button
className={`side-button ${side}`}
onClick={() => handlePlusButtonClick(side)}
title={
selectedZone.activeSides.includes(side)
? `Remove all items and close ${side} panel`
: `Activate ${side} panel`
}
>
+
</button>
{/* Extra Buttons */}
{selectedZone.activeSides.includes(side) && (
<div className="extra-Bs">
{/* Hide Panel */}
<div
className={`icon ${
hiddenPanels.includes(side) ? "active" : ""
}`}
title={
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
}
onClick={() => toggleVisibility(side)}
>
<EyeIcon />
</div>
{/* Clean Panel */}
<div
className="icon"
title="Clean Panel"
onClick={() => cleanPanel(side)}
>
<CleanPannel />
</div>
{/* Lock/Unlock Panel */}
<div
className={`icon ${
selectedZone.lockedPanels.includes(side) ? "active" : ""
}`}
title={
selectedZone.lockedPanels.includes(side)
? "Unlock Panel"
: "Lock Panel"
}
onClick={() => toggleLockPanel(side)}
>
<LockIcon />
</div>
</div>
)}
</div>
))}
</div>
);
};
export default AddButtons;

View File

@@ -0,0 +1,179 @@
import React, { useEffect, useRef } from "react";
import { Widget } from "../../../store/useWidgetStore";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
interface DisplayZoneProps {
zonesData: {
[key: string]: {
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
};
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
}>
>;
}
const DisplayZone: React.FC<DisplayZoneProps> = ({
zonesData,
selectedZone,
setSelectedZone,
}) => {
// Ref for the container element
const containerRef = useRef<HTMLDivElement | null>(null);
// Example state for selectedOption and options (adjust based on your actual use case)
const [selectedOption, setSelectedOption] = React.useState<string | null>(
null
);
console.log('setSelectedOption: ', setSelectedOption);
const [options, setOptions] = React.useState<string[]>([]);
console.log('setOptions: ', setOptions);
// Scroll to the selected option when it changes
useEffect(() => {
const container = containerRef.current;
if (container && selectedOption) {
// Handle scrolling to the selected option
const index = options.findIndex((option) => {
const formattedOption = formatOptionName(option);
const selectedFormattedOption =
selectedOption?.split("_")[1] || selectedOption;
return formattedOption === selectedFormattedOption;
});
if (index !== -1) {
const optionElement = container.children[index] as HTMLElement;
if (optionElement) {
optionElement.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center",
});
}
}
}
}, [selectedOption, options]);
useEffect(() => {
const container = containerRef.current;
const handleWheel = (event: WheelEvent) => {
event.preventDefault();
if (container) {
container.scrollBy({
left: event.deltaY * 2, // Adjust the multiplier for faster scrolling
behavior: "smooth",
});
}
};
let isDragging = false;
let startX: number;
let scrollLeft: number;
const handleMouseDown = (event: MouseEvent) => {
isDragging = true;
startX = event.pageX - (container?.offsetLeft || 0);
scrollLeft = container?.scrollLeft || 0;
};
const handleMouseMove = (event: MouseEvent) => {
if (!isDragging || !container) return;
event.preventDefault();
const x = event.pageX - (container.offsetLeft || 0);
const walk = (x - startX) * 2; // Adjust the multiplier for faster dragging
container.scrollLeft = scrollLeft - walk;
};
const handleMouseUp = () => {
isDragging = false;
};
const handleMouseLeave = () => {
isDragging = false;
};
if (container) {
container.addEventListener("wheel", handleWheel, { passive: false });
container.addEventListener("mousedown", handleMouseDown);
container.addEventListener("mousemove", handleMouseMove);
container.addEventListener("mouseup", handleMouseUp);
container.addEventListener("mouseleave", handleMouseLeave);
}
return () => {
if (container) {
container.removeEventListener("wheel", handleWheel);
container.removeEventListener("mousedown", handleMouseDown);
container.removeEventListener("mousemove", handleMouseMove);
container.removeEventListener("mouseup", handleMouseUp);
container.removeEventListener("mouseleave", handleMouseLeave);
}
};
}, []);
// Helper function to format option names (customize as needed)
const formatOptionName = (option: string): string => {
// Replace underscores with spaces and capitalize the first letter
return option.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase());
};
return (
<div
ref={containerRef}
className={`zoon-wrapper ${
selectedZone.activeSides.includes("bottom") && "bottom"
}`}
>
{Object.keys(zonesData).map((zoneName, index) => (
<div
key={index}
className={`zone ${
selectedZone.zoneName === zoneName ? "active" : ""
}`}
onClick={() => {
setSelectedZone({
zoneName,
...zonesData[zoneName],
});
}}
>
{zoneName}
</div>
))}
</div>
);
};
export default DisplayZone;

View File

@@ -0,0 +1,82 @@
import { useWidgetStore } from "../../../store/useWidgetStore";
import PieGraphComponent from "../charts/PieGraphComponent";
import BarGraphComponent from "../charts/BarGraphComponent";
import LineGraphComponent from "../charts/LineGraphComponent";
export const DraggableWidget = ({ widget }: { widget: any }) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const handlePointerDown = () => {
if (selectedChartId?.id !== widget.id) {
setSelectedChartId(widget);
}
};
return (
<>
<div
key={widget.id}
className={`chart-container ${
selectedChartId?.id === widget.id && "activeChart"
}`}
onPointerDown={handlePointerDown}
>
{widget.type === "progress" ? (
// <ProgressCard title={widget.title} data={widget.data} />
<></>
) : (
<>
{widget.type === "line" && (
<LineGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "bar" && (
<BarGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "pie" && (
<PieGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
</>
)}
</div>
</>
);
};

View File

@@ -0,0 +1,203 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useWidgetStore } from "../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
interface PanelProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
}>
>;
}
const generateUniqueId = () =>
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const Panel: React.FC<PanelProps> = ({ selectedZone, setSelectedZone }) => {
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
const getPanelStyle = useMemo(
() => (side: Side) => {
const currentIndex = selectedZone.panelOrder.indexOf(side);
const previousPanels = selectedZone.panelOrder.slice(0, currentIndex);
const leftActive = previousPanels.includes("left");
const rightActive = previousPanels.includes("right");
const topActive = previousPanels.includes("top");
const bottomActive = previousPanels.includes("bottom");
switch (side) {
case "top":
case "bottom":
return {
width: `calc(100% - ${
(leftActive ? 204 : 0) + (rightActive ? 204 : 0)
}px)`,
left: leftActive ? "204px" : "0",
right: rightActive ? "204px" : "0",
[side]: "0",
height: "200px",
};
case "left":
case "right":
return {
height: `calc(100% - ${
(topActive ? 204 : 0) + (bottomActive ? 204 : 0)
}px)`,
top: topActive ? "204px" : "0",
bottom: bottomActive ? "204px" : "0",
[side]: "0",
width: "200px",
};
default:
return {};
}
},
[selectedZone.panelOrder]
);
const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault();
const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return;
if (isPanelLocked(panel)) return;
const currentWidgetsCount = getCurrentWidgetCount(panel);
const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return;
addWidgetToPanel(draggedAsset, panel);
};
// Helper functions
const isPanelLocked = (panel: Side) =>
selectedZone.lockedPanels.includes(panel);
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter(w => w.panel === panel).length;
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = 200;
const CHART_HEIGHT = 200;
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
}
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
};
const addWidgetToPanel = (asset: any, panel: Side) => {
const newWidget = {
...asset,
id: generateUniqueId(),
panel,
};
setSelectedZone(prev => ({
...prev,
widgets: [...prev.widgets, newWidget]
}));
};
useEffect(() => {
const observers: ResizeObserver[] = [];
const currentPanelRefs = panelRefs.current;
selectedZone.activeSides.forEach((side) => {
const element = currentPanelRefs[side];
if (element) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
setPanelDimensions((prev) => ({
...prev,
[side]: { width, height },
}));
}
});
observer.observe(element);
observers.push(observer);
}
});
return () => {
observers.forEach((observer) => observer.disconnect());
};
}, [selectedZone.activeSides]);
const { isPlaying } = usePlayButtonStore();
return (
<>
{selectedZone.activeSides.map((side) => (
<div
key={side}
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()}
ref={(el) => {
if (el) {
panelRefs.current[side] = el;
} else {
delete panelRefs.current[side];
}
}}
>
<div
className={`panel-content ${isPlaying && "fullScreen"}`}
style={{
pointerEvents: selectedZone.lockedPanels.includes(side)
? "none"
: "auto",
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
}}
>
<>{}</>
{selectedZone.widgets
.filter((w) => w.panel === side)
.map((widget) => (
<DraggableWidget widget={widget} key={widget.id} />
))}
</div>
</div>
))}
</>
);
};
export default Panel;

View File

@@ -0,0 +1,103 @@
import React, { useEffect, useState, useRef } from "react";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import Panel from "./Panel";
import AddButtons from "./AddButtons";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
import DisplayZone from "./DisplayZone";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
const RealTimeVisulization: React.FC = () => {
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
const containerRef = useRef<HTMLDivElement>(null);
const [zonesData, setZonesData] = useState<{
[key: string]: {
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
}>({
"Manufacturing unit": {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
"Assembly unit": {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
"Packing unit": {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
Warehouse: {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
Inventory: {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
});
const { isPlaying } = usePlayButtonStore();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
useEffect(() => {
setZonesData((prev) => ({
...prev,
[selectedZone.zoneName]: selectedZone,
}));
}, [selectedZone]);
return (
<div
ref={containerRef}
id="real-time-vis-canvas"
className="realTime-viz canvas"
style={{
height: isPlaying ? "100vh" : "",
width: isPlaying ? "100%" : "",
left: isPlaying ? "0%" : "",
}}
>
<DisplayZone
zonesData={zonesData}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
{!isPlaying && (
<AddButtons
hiddenPanels={hiddenPanels}
setHiddenPanels={setHiddenPanels}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
)}
<Panel selectedZone={selectedZone} setSelectedZone={setSelectedZone} />
</div>
);
};
export default RealTimeVisulization;

View File

@@ -0,0 +1,23 @@
import React from "react";
import RegularDropDown from "./RegularDropDown";
import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
const EyeDropInput: React.FC = () => {
return (
<div className="eye-dropper-input-container">
<div className="label">Object</div>
<div className="input-container">
<RegularDropDown
header="select object"
options={[]}
onSelect={() => {}}
/>
<div className="eye-picker-button">
<EyeDroperIcon isActive={false} />
</div>
</div>
</div>
);
};
export default EyeDropInput;

View File

@@ -0,0 +1,69 @@
import React, { useEffect, useState } from "react";
interface InputToggleProps {
label: string; // Represents the toggle state (on/off)
min?: number;
max?: number;
onClick?: () => void; // Function to handle toggle clicks
onChange?: (value: number) => void; // Function to handle toggle clicks
disabled?: boolean;
value?: number;
}
const InputRange: React.FC<InputToggleProps> = ({
label,
onClick,
onChange,
min = 0,
max = 10,
disabled,
value = 5,
}) => {
const [rangeValue, setRangeValue] = useState<number>(value);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const newValue = parseInt(e.target.value); // Parse the value to an integer
setRangeValue(newValue); // Update the local state
if (onChange) {
onChange(newValue); // Call the onChange function if it exists
}
}
useEffect(() => {
setRangeValue(value);
}, [value]);
return (
<div className="input-range-container">
<label
htmlFor={`range-input ${value}`}
className="label"
onClick={onClick}
>
{label}
</label>
<div className="input-container">
<input
id={`range-input ${value}`}
type="range"
min={min}
max={max}
onChange={handleChange}
disabled={disabled}
value={rangeValue}
/>
<input
type="number"
min={min}
className="input-value"
max={max}
value={rangeValue}
onChange={handleChange}
disabled={disabled}
/>
</div>
</div>
);
};
export default InputRange;

View File

@@ -0,0 +1,51 @@
import React, { useEffect, useState } from "react";
interface InputToggleProps {
label: string; // Represents the toggle state (on/off)
onClick?: () => void; // Function to handle toggle clicks
value?: boolean;
inputKey: string;
}
const InputToggle: React.FC<InputToggleProps> = ({
label,
onClick,
value = false,
inputKey,
}) => {
const [activeValue, setActiveValue] = useState<boolean>(value);
function handleOnClick() {
setActiveValue(!activeValue);
if (onClick) onClick();
}
useEffect(() => {
setActiveValue(value);
}, [value]);
return (
<div className="input-toggle-container">
<label htmlFor={`toogle-input-${inputKey}`} className="label">
{label}
</label>
<div className={"check-box"} onClick={handleOnClick}>
<div
className="check-box-style"
style={{
left: activeValue ? "50%" : "2px",
background: activeValue ? "" : "var(--text-disabled)",
}}
></div>
<input
type="checkbox"
name=""
id={`toogle-input-${inputKey}`}
defaultChecked={value}
/>
</div>
</div>
);
};
export default InputToggle;

View File

@@ -0,0 +1,76 @@
import React, { useState } from "react";
import RenameInput from "./RenameInput";
type InputWithDropDownProps = {
label: string;
value: string;
options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option
onClick?: () => void;
onChange: (newValue: string) => void;
editableLabel?: boolean;
};
const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
label,
value,
options,
activeOption,
onClick,
onChange,
editableLabel = false,
}) => {
const separatedWords = label
.split(/(?=[A-Z])/)
.map((word) => word.trim())
.toString();
const [openDropdown, setOpenDropdown] = useState(false);
return (
<div className="value-field-container">
{editableLabel ? (
<RenameInput value={label} />
) : (
<label htmlFor={separatedWords} className="label">
{label}
</label>
)}
<div className="input default" id={separatedWords}>
<input
type="text"
defaultValue={value}
onChange={(e) => {
onChange(e.target.value);
}}
/>
{activeOption && (
<div
className="dropdown"
onClick={() => {
setOpenDropdown(true);
}}
>
<div className="active-option">{activeOption}</div>
{options && openDropdown && (
<div className="dropdown-options-list">
{options.map((option, index) => (
<div
key={index}
className={"dropdown-option"}
onClick={onClick}
>
{option}
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
);
};
export default InputWithDropDown;

View File

@@ -0,0 +1,26 @@
import React from "react";
interface LabeledButtonProps {
label: string; // Label for the button
onClick?: () => void; // Function to call when the button is clicked
disabled?: boolean; // Optional prop to disable the button
value?: string;
}
const LabeledButton: React.FC<LabeledButtonProps> = ({
label,
onClick,
disabled = false,
value = "Click here",
}) => {
return (
<div className="labeled-button-container">
<div className="label">{label}</div>
<button className="button" onClick={onClick} disabled={disabled}>
{value}
</button>
</div>
);
};
export default LabeledButton;

View File

@@ -0,0 +1,29 @@
import React, { useState } from "react";
import RegularDropDown from "./RegularDropDown";
type LabledDropdownProps = {
defaultOption: string; // Initial active option
options: string[]; // Array of dropdown options
};
const LabledDropdown: React.FC<LabledDropdownProps> = ({ defaultOption, options }) => {
const [activeOption, setActiveOption] = useState(defaultOption); // State for active option
const handleSelect = (option: string) => {
setActiveOption(option); // Update the active option state
};
return (
<div className="value-field-container">
<div className="label">Type</div>
<RegularDropDown
header={activeOption} // Display the current active option
options={options} // Use the options from props
onSelect={handleSelect} // Handle option selection
search = {false}
/>
</div>
);
};
export default LabledDropdown;

View File

@@ -0,0 +1,71 @@
import React, { useState } from "react";
const MultiEmailInvite: React.FC = () => {
const [emails, setEmails] = useState<string[]>([]);
const [inputValue, setInputValue] = useState("");
const handleAddEmail = () => {
const trimmedEmail = inputValue.trim();
// Validate email
if (!trimmedEmail || !validateEmail(trimmedEmail)) {
alert("Please enter a valid email address.");
return;
}
// Check for duplicates
if (emails.includes(trimmedEmail)) {
alert("This email has already been added.");
return;
}
// Add email to the list
setEmails([...emails, trimmedEmail]);
setInputValue(""); // Clear the input field after adding
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" || e.key === ",") {
e.preventDefault();
handleAddEmail();
}
};
const handleRemoveEmail = (emailToRemove: string) => {
setEmails(emails.filter((email) => email !== emailToRemove));
};
const validateEmail = (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const [inputFocus, setInputFocus] = useState(false);
return (
<div className="multi-email-invite-input-container">
<div className={`multi-email-invite-input${inputFocus ? " active" : ""}`}>
{emails.map((email, index) => (
<div key={index} className="entered-emails">
{email}
<span onClick={() => handleRemoveEmail(email)}>&times;</span>
</div>
))}
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onFocus={() => setInputFocus(true)}
onBlur={() => setInputFocus(false)}
onKeyDown={handleKeyDown}
placeholder="Enter email and press Enter or comma"
/>
</div>
<div onClick={handleAddEmail} className="invite-button">
Invite
</div>
</div>
);
};
export default MultiEmailInvite;

View File

@@ -0,0 +1,141 @@
import React, { useState, useRef, useEffect } from "react";
// Dropdown Item Component
const DropdownItem = ({
label,
href,
onClick,
}: {
label: string;
href?: string;
onClick?: () => void;
}) => (
<a
href={href || "#"}
className="dropdown-item"
onClick={(e) => {
e.preventDefault();
onClick?.();
}}
>
{label}
</a>
);
// Nested Dropdown Component
const NestedDropdown = ({
label,
children,
onSelect,
}: {
label: string;
children: React.ReactNode;
onSelect: (selectedLabel: string) => void;
}) => {
const [open, setOpen] = useState(false);
return (
<div className="nested-dropdown">
{/* Dropdown Trigger */}
<div
className={`dropdown-trigger ${open ? "open" : ""}`}
onClick={() => setOpen(!open)} // Toggle submenu on click
>
{label} <span className="icon">{open ? "▼" : "▶"}</span>
</div>
{/* Submenu */}
{open && (
<div className="submenu">
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
// Clone the element and pass the `onSelect` prop only if it's expected
return React.cloneElement(child as React.ReactElement<any>, { onSelect });
}
return child; // Return non-element children as-is
})}
</div>
)}
</div>
);
};
// Recursive Function to Render Nested Data
const renderNestedData = (
data: Record<string, any>,
onSelect: (selectedLabel: string) => void
) => {
return Object.entries(data).map(([key, value]) => {
if (typeof value === "object" && !Array.isArray(value)) {
// If the value is an object, render it as a nested dropdown
return (
<NestedDropdown key={key} label={key} onSelect={onSelect}>
{renderNestedData(value, onSelect)}
</NestedDropdown>
);
} else if (Array.isArray(value)) {
// If the value is an array, render each item as a dropdown item
return value.map((item, index) => (
<DropdownItem key={index} label={item} onClick={() => onSelect(item)} />
));
} else {
// If the value is a simple string, render it as a dropdown item
return (
<DropdownItem key={key} label={value} onClick={() => onSelect(value)} />
);
}
});
};
// Main Multi-Level Dropdown Component
const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
const [open, setOpen] = useState(false);
const [selectedLabel, setSelectedLabel] = useState("Dropdown trigger");
const dropdownRef = useRef<HTMLDivElement>(null);
// Handle outer click to close the dropdown
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
// Handle selection of an item
const handleSelect = (selectedLabel: string) => {
setSelectedLabel(selectedLabel); // Update the dropdown trigger text
setOpen(false); // Close the dropdown
};
return (
<div className="multi-level-dropdown" ref={dropdownRef}>
{/* Dropdown Trigger Button */}
<button
className={`dropdown-button ${open ? "open" : ""}`}
onClick={() => setOpen(!open)} // Toggle main menu on click
>
{selectedLabel} <span className="icon"></span>
</button>
{/* Dropdown Menu */}
{open && (
<div className="dropdown-menu">
<div className="dropdown-content">
{renderNestedData(data, handleSelect)}
</div>
</div>
)}
</div>
);
};
export default MultiLevelDropdown;

View File

@@ -0,0 +1,127 @@
import React, { useState, useEffect, useRef } from "react";
interface DropdownProps {
header: string;
options: string[];
onSelect: (option: string) => void;
search?: boolean;
onClick?: () => void;
onChange?: () => void;
}
const RegularDropDown: React.FC<DropdownProps> = ({
header,
options,
onSelect,
search = true,
onClick,
onChange,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState(""); // State to store search term
const [filteredOptions, setFilteredOptions] = useState<string[]>(options); // State for filtered options
const dropdownRef = useRef<HTMLDivElement>(null); // Ref for the dropdown container
// Reset selectedOption when the dropdown closes
useEffect(() => {
if (!isOpen) {
setSelectedOption(null);
setSearchTerm(""); // Clear the search term when the dropdown closes
setFilteredOptions(options); // Reset filtered options when the dropdown closes
}
}, [isOpen, options]);
// Reset selectedOption when the header prop changes
useEffect(() => {
setSelectedOption(null);
setSearchTerm(""); // Reset search term if header changes
setFilteredOptions(options); // Reset options if header changes
}, [header, options]);
// Close dropdown if clicked outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, []);
// Toggle the dropdown
const toggleDropdown = () => {
setIsOpen((prev) => !prev);
};
// Handle option selection
const handleOptionClick = (option: string) => {
setSelectedOption(option);
onSelect(option);
setIsOpen(false);
};
// Handle search input change
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const term = event.target.value;
setSearchTerm(term);
// Filter options based on the search term
const filtered = options.filter((option) =>
option.toLowerCase().includes(term.toLowerCase())
);
setFilteredOptions(filtered);
};
return (
<div className="regularDropdown-container" ref={dropdownRef}>
{/* Dropdown Header */}
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
<div className="key">{selectedOption || header}</div>
<div className="icon"></div>
</div>
{/* Dropdown Options */}
{isOpen && (
<div className="dropdown-options">
{/* Search Bar */}
{search && (
<div className="dropdown-search">
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={handleSearchChange}
/>
</div>
)}
{/* Filtered Options */}
{filteredOptions.length > 0 ? (
filteredOptions.map((option, index) => (
<div
className="option"
key={index}
onClick={() => handleOptionClick(option)}
>
{option}
</div>
))
) : (
<div className="no-options">No options found</div>
)}
</div>
)}
</div>
);
};
export default RegularDropDown;

View File

@@ -0,0 +1,59 @@
import React, { useState, useRef } from "react";
interface RenameInputProps {
value: string;
onRename?: (newText: string) => void;
}
const RenameInput: React.FC<RenameInputProps> = ({ value, onRename }) => {
const [isEditing, setIsEditing] = useState(false);
const [text, setText] = useState(value);
const inputRef = useRef<HTMLInputElement | null>(null);
const handleDoubleClick = () => {
setIsEditing(true);
setTimeout(() => inputRef.current?.focus(), 0); // Focus the input after rendering
};
const handleBlur = () => {
setIsEditing(false);
if (onRename) {
onRename(text);
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
setIsEditing(false);
if (onRename) {
onRename(text);
}
}
};
return (
<>
{isEditing ? (
<input
ref={inputRef}
type="text"
value={text}
onChange={handleChange}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
className="rename-input"
/>
) : (
<span onDoubleClick={handleDoubleClick} className="input-value">
{text}
</span>
)}
</>
);
};
export default RenameInput;

View File

@@ -0,0 +1,67 @@
import React, { ChangeEvent, useState } from "react";
import { CloseIcon, SearchIcon } from "../../icons/ExportCommonIcons";
interface SearchProps {
value?: string; // The current value of the search input
placeholder?: string; // Placeholder text for the input
onChange: (value: string) => void; // Callback function to handle input changes
}
const Search: React.FC<SearchProps> = ({
value = "",
placeholder = "Search",
onChange,
}) => {
// State to track the input value and focus status
const [inputValue, setInputValue] = useState(value);
const [isFocused, setIsFocused] = useState(false);
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
setInputValue(newValue);
onChange(newValue); // Call the onChange prop with the new value
};
const handleClear = () => {
setInputValue("");
onChange(""); // Clear the input value
};
const handleFocus = () => {
setIsFocused(true); // Set focus state to true
};
const handleBlur = () => {
setIsFocused(false); // Set focus state to false
};
return (
<div className="search-wrapper">
<div
className={`search-container ${
isFocused || inputValue ? "active" : ""
}`}
>
<div className="icon-container">
<SearchIcon />
</div>
<input
type="text"
className="search-input"
value={inputValue}
placeholder={placeholder}
onChange={handleInputChange}
onFocus={handleFocus}
onBlur={handleBlur}
/>
{inputValue && (
<button className="clear-button" onClick={handleClear}>
<CloseIcon />
</button>
)}
</div>
</div>
);
};
export default Search;

View File

@@ -0,0 +1,31 @@
import React from "react";
interface ToggleHeaderProps {
options: string[]; // Array of strings representing the options
activeOption: string; // The currently active option
handleClick: (option: string) => void; // Function to handle click events
}
const ToggleHeader: React.FC<ToggleHeaderProps> = ({
options,
activeOption,
handleClick,
}) => {
return (
<div className="toggle-header-container">
{options.map((option, index) => (
<div
key={index}
className={`toggle-header-item ${
option === activeOption ? "active" : ""
}`}
onClick={() => handleClick(option)} // Call handleClick when an option is clicked
>
{option}
</div>
))}
</div>
);
};
export default ToggleHeader;

View File

@@ -0,0 +1,92 @@
import React, { useState } from "react";
import List from "./List";
import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
interface DropDownListProps {
value?: string; // Value to display in the DropDownList
items?: { id: string; name: string }[]; // Items to display in the dropdown list
showFocusIcon?: boolean; // Determines if the FocusIcon should be displayed
showAddIcon?: boolean; // Determines if the AddIcon should be displayed
showKebabMenu?: boolean; // Determines if the KebabMenuList should be displayed
kebabMenuItems?: { id: string; name: string }[]; // Items for the KebabMenuList
defaultOpen?: boolean; // Determines if the dropdown list should be open by default
listType?: string; // Type of list to display
}
const DropDownList: React.FC<DropDownListProps> = ({
value = "Dropdown",
items = [],
showFocusIcon = false,
showAddIcon = true,
showKebabMenu = true,
kebabMenuItems = [
{ id: "Buildings", name: "Buildings" },
{ id: "Paths", name: "Paths" },
{ id: "Zones", name: "Zones" },
],
defaultOpen = false,
listType = "default",
}) => {
const [isOpen, setIsOpen] = useState<boolean>(defaultOpen);
const handleToggle = () => {
setIsOpen((prev) => !prev); // Toggle the state
};
return (
<div className="dropdown-list-container">
<div className="head">
<div className="value" onClick={handleToggle}>
{value}
</div>
<div className="options">
{showFocusIcon && (
<div className="focus option">
<FocusIcon />
</div>
)}
{showAddIcon && (
<div className="add option">
<AddIcon />
</div>
)}
{showKebabMenu && (
<div className="kebab-menu option">
<KebabMenuListMultiSelect items={kebabMenuItems} />
</div>
)}
<div
className="collapse-icon option"
style={{ transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)" }}
onClick={handleToggle}
>
<ArrowIcon />
</div>
</div>
</div>
{isOpen && (
<div className="lists-container">
{listType === "default" && <List items={items} />}
{listType === "outline" && (
<>
<DropDownList
value="Buildings"
showKebabMenu={false}
showAddIcon={false}
/>
<DropDownList
value="Zones"
showKebabMenu={false}
showAddIcon={false}
items={[]}
/>
</>
)}
</div>
)}
</div>
);
};
export default DropDownList;

View File

@@ -0,0 +1,45 @@
import React, { useState } from "react";
import { KebebIcon } from "../../icons/ExportCommonIcons";
interface KebabMenuListProps {
items: string[]; // Array of menu items
onSelect?: (item: string) => void; // Callback when a menu item is selected
}
const KebabMenuList: React.FC<KebabMenuListProps> = ({ items, onSelect }) => {
const [isOpen, setIsOpen] = useState(false);
const handleToggle = () => {
setIsOpen((prev) => !prev);
};
const handleItemClick = (item: string) => {
if (onSelect) {
onSelect(item);
}
setIsOpen(false); // Close menu after selection
};
return (
<div className="kebab-menu-container">
<div className="kebab-icon" onClick={handleToggle}>
<KebebIcon />
</div>
{isOpen && (
<div className="menu-list">
{items.map((item, index) => (
<div
key={index}
className="menu-item"
onClick={() => handleItemClick(item)}
>
{item}
</div>
))}
</div>
)}
</div>
);
};
export default KebabMenuList;

View File

@@ -0,0 +1,82 @@
import React, { useEffect, useRef, useState } from "react";
import { KebebIcon, TickIcon } from "../../icons/ExportCommonIcons";
interface KebabMenuListMultiSelectProps {
items: { id: string; name: string }[]; // Array of menu items with id and name
onSelectionChange?: (selectedItems: string[]) => void; // Callback for selected items
}
const KebabMenuListMultiSelect: React.FC<KebabMenuListMultiSelectProps> = ({
items,
onSelectionChange,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedItems, setSelectedItems] = useState<string[]>([]);
const menuRef = useRef<HTMLDivElement>(null); // Ref to track the container
const handleToggle = () => {
setIsOpen((prev) => !prev);
};
const handleItemToggle = (id: string) => {
setSelectedItems((prevSelected) => {
const isAlreadySelected = prevSelected.includes(id);
const updatedSelection = isAlreadySelected
? prevSelected.filter((item) => item !== id) // Deselect if already selected
: [...prevSelected, id]; // Add to selection if not selected
if (onSelectionChange) {
onSelectionChange(updatedSelection);
}
return updatedSelection;
});
};
// Close menu if clicked outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
return (
<div className="kebab-menu-container" ref={menuRef}>
<div className="kebab-icon" onClick={handleToggle}>
<KebebIcon />
</div>
{isOpen && (
<div className="menu-list">
{items.map((item) => (
<div
key={item.id}
className={`menu-item ${
selectedItems.includes(item.id) ? "selected" : ""
}`}
onClick={() => handleItemToggle(item.id)}
>
<input
type="checkbox"
checked={selectedItems.includes(item.id)}
onChange={() => handleItemToggle(item.id)}
/>
<div className="icon-container">
{selectedItems.includes(item.id) && <TickIcon />}
</div>
{item.name}
</div>
))}
</div>
)}
</div>
);
};
export default KebabMenuListMultiSelect;

View File

@@ -0,0 +1,45 @@
import React from "react";
import RenameInput from "../inputs/RenameInput";
import { EyeIcon, LockIcon, RmoveIcon } from "../../icons/ExportCommonIcons";
interface ListProps {
items?: { id: string; name: string }[]; // Optional array of items to render
placeholder?: string; // Optional placeholder text
}
const List: React.FC<ListProps> = ({ items = [] }) => {
return (
<>
{items.length > 0 ? (
<ul className="list-wrapper">
{items.map((item, index) => (
<li key={index} className="list-container">
<div className="list-item">
<div className="value">
<RenameInput value={item.name} />
</div>
<div className="options-container">
<div className="lock option">
<LockIcon isLocked />
</div>
<div className="visibe option">
<EyeIcon isClosed />
</div>
<div className="remove option">
<RmoveIcon />
</div>
</div>
</div>
</li>
))}
</ul>
) : (
<div className="list-wrapper">
<div className="no-item">No items to display</div>
</div>
)}
</>
);
};
export default List;

View File

@@ -0,0 +1,94 @@
import { useMemo } from "react";
import { Bar } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const LineGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1",
borderColor: "#ffffff",
borderWidth: 2,
fill: false,
},
],
};
return <Bar data={chartData} options={options} />;
};
export default LineGraphComponent;

View File

@@ -0,0 +1,93 @@
import { useMemo } from "react";
import { Doughnut, Line } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const DoughnutGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
};
return <Doughnut data={chartData} options={options} />;
};
export default DoughnutGraphComponent;

View File

@@ -0,0 +1,96 @@
import { useMemo } from "react";
import { Line } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const LineGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: true, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
};
return <Line data={chartData} options={options} />;
};
export default LineGraphComponent;
// like this

View File

@@ -0,0 +1,91 @@
import { useMemo } from "react";
import { Pie } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const PieChartComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
// Access the CSS variable for the primary accent color
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue("--accent-color")
.trim();
console.log("accentColor: ", accentColor);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#ffffff",
borderWidth: 2,
},
],
};
return <Pie data={chartData} options={options} />;
};
export default PieChartComponent;

View File

@@ -0,0 +1,93 @@
import { useMemo } from "react";
import { Line, PolarArea } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const PolarAreaGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
};
return <PolarArea data={chartData} options={options} />;
};
export default PolarAreaGraphComponent;

View File

@@ -0,0 +1,25 @@
const ProgressCard = ({
title,
data,
}: {
title: string;
data: { stocks: Array<{ key: string; value: number; description: string }> };
}) => (
<div className="chart progressBar">
<div className="header">{title}</div>
{data?.stocks.map((stock, index) => (
<div key={index} className="stock">
<span className="stock-item">
<span className="stockValues">
<div className="key">{stock.key}</div>
<div className="value">{stock.value}</div>
</span>
<div className="stock-description">{stock.description}</div>
</span>
<div className="icon">Icon</div>
</div>
))}
</div>
);
export default ProgressCard;

View File

@@ -0,0 +1,104 @@
import React, { useMemo } from "react";
import { Radar } from "react-chartjs-2";
import {
ChartOptions,
ChartData,
RadialLinearScaleOptions,
} from "chart.js";
interface ChartComponentProps {
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: number[]; // Expecting an array of numbers for radar chart data
}
const RadarGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
data,
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [
fontSize,
]);
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [
fontWeight,
chartFontWeightMap,
]);
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options: ChartOptions<"radar"> = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
position: "top",
},
},
scales: {
r: {
min: 0,
max: 100,
angleLines: {
display: true,
},
ticks: {
display: true,
stepSize: 20,
},
} as RadialLinearScaleOptions,
},
}),
[title, chartFontStyle]
);
const chartData: ChartData<"radar"> = useMemo(
() => ({
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "Dataset 1",
data, // Use the data passed as a prop
backgroundColor: "rgba(111, 66, 193, 0.2)",
borderColor: "#6f42c1",
borderWidth: 2,
fill: true,
},
],
}),
[data]
);
return <Radar data={chartData} options={options} />;
};
export default RadarGraphComponent;

View File

@@ -0,0 +1,35 @@
const FleetEfficiency = () => {
const progress = 75; // Example progress value (0-100)
// Calculate the rotation angle for the progress bar
const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°)
return (
<div className="fleetEfficiency floating">
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
<div className="progress">
<div className="barOverflow">
{/* Apply dynamic rotation to the bar */}
<div
className="bar"
style={{ transform: `rotate(${rotationAngle}deg)` }}
></div>
</div>
</div>
</div>
<div className="scaleLabels">
<span>0%</span>
<div className="centerText">
<div className="percentage">{progress}%</div>
<div className="status">Optimal</div>
</div>
<span>100%</span>
</div>
</div>
);
};
export default FleetEfficiency;

View File

@@ -0,0 +1,86 @@
import React from "react";
interface ProductivityData {
distancePerTask: number;
spaceUtilization: number;
taskCompletionTime: string;
}
const ProductivityDashboard: React.FC = () => {
const data: ProductivityData = {
distancePerTask: 45,
spaceUtilization: 72,
taskCompletionTime: "7:44",
};
// Function to calculate the stroke dash offset for the circular progress
const calculateDashOffset = (percentage: number, circumference: number) => {
return circumference - (percentage / 100) * circumference;
};
// Constants for the circular progress chart
const radius = 60; // Radius of the circle
const strokeWidth = 10; // Thickness of the stroke
const diameter = radius * 2; // Diameter of the circle
const circumference = Math.PI * (radius * 2); // Circumference of the circle
return (
<div className="productivity-dashboard">
<header>
<h2>Productivity</h2>
<div className="options">...</div>
</header>
<main>
<section className="metrics">
<div className="metric">
<p className="label">Distance per Task</p>
<p className="value">{data.distancePerTask} m</p>
</div>
<div className="metric">
<p className="label">Space Utilization</p>
<p className="value">{data.spaceUtilization}%</p>
</div>
</section>
<section className="chart-section">
<svg
className="progress-circle"
width={diameter}
height={diameter}
viewBox={`0 0 ${diameter} ${diameter}`}
>
{/* Background Circle */}
<circle
cx={radius}
cy={radius}
r={radius - strokeWidth / 2}
fill="transparent"
stroke="#6c757d"
strokeWidth={strokeWidth}
/>
{/* Progress Circle */}
<circle
cx={radius}
cy={radius}
r={radius - strokeWidth / 2}
fill="transparent"
stroke="#2ecc71"
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={calculateDashOffset(data.spaceUtilization, circumference)}
strokeLinecap="round"
transform={`rotate(-90 ${radius} ${radius})`}
/>
</svg>
<div className="chart-details">
<p className="title">Task Completion Time</p>
<p className="time">{data.taskCompletionTime}</p>
<p className="subtitle">Total Score</p>
</div>
</section>
</main>
</div>
);
};
export default ProductivityDashboard;

View File

@@ -0,0 +1,33 @@
import React from "react";
interface SimpleCardProps {
header: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
value: string;
per: string; // Percentage change
}
const SimpleCard: React.FC<SimpleCardProps> = ({
header,
icon: Icon,
value,
per,
}) => {
return (
<div className="floating total-card" draggable>
<div className="header-wrapper">
<div className="header">{header}</div>
<div className="data-values">
<div className="value">{value}</div>
<div className="per">{per}</div>
</div>
</div>
<div className="icon">
<Icon />
</div>
</div>
);
};
export default SimpleCard;

View File

@@ -0,0 +1,130 @@
import React from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler, // Import Filler for area fill
} from "chart.js";
// Register ChartJS components
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler
);
const WarehouseThroughput = () => {
// Line graph data for a year (monthly throughput)
const lineGraphData = {
labels: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
], // Months of the year
datasets: [
{
label: "Throughput (units/month)",
data: [500, 400, 300, 450, 350, 250, 200, 300, 250, 150, 100, 150], // Example monthly data
borderColor: "#6f42c1", // Use the desired color for the line (purple)
backgroundColor: "rgba(111, 66, 193, 0.2)", // Use a semi-transparent purple for the fill
borderWidth: 2, // Line thickness
fill: true, // Enable fill for this dataset
pointRadius: 0, // Remove dots at each data point
tension: 0.5, // Smooth interpolation for the line
},
],
};
// Line graph options
const lineGraphOptions = {
responsive: true,
maintainAspectRatio: false, // Allow custom height/width adjustments
plugins: {
legend: {
display: false, // Hide legend
},
title: {
display: false, // No chart title needed
},
tooltip: {
callbacks: {
label: (context: any) => {
const value = context.parsed.y;
return `${value} units`; // Customize tooltip to display "units"
},
},
},
},
scales: {
x: {
grid: {
display: false, // Hide x-axis grid lines
},
ticks: {
maxRotation: 0, // Prevent label rotation
autoSkip: false, // Display all months
font: {
size: 8, // Adjust font size for readability
color: "#ffffff", // Light text color for labels
},
},
},
y: {
display: true, // Show y-axis
grid: {
drawBorder: false, // Remove border line
color: "rgba(255, 255, 255, 0.2)", // Light gray color for grid lines
borderDash: [5, 5], // Dotted line style (array defines dash and gap lengths)
},
ticks: {
font: {
size: 8, // Adjust font size for readability
color: "#ffffff", // Light text color for ticks
},
},
},
},
elements: {
line: {
tension: 0.5, // Smooth interpolation for the line
},
},
};
return (
<div className="warehouseThroughput floating" draggable>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
<span>(+5) more</span> in 2025
</p>
</div>
<div className="lineGraph" style={{ height: "100%" }}>
{/* Line Graph */}
<Line data={lineGraphData} options={lineGraphOptions} />
</div>
</div>
);
};
export default WarehouseThroughput;