From 06fa09bb4288cedb4c2714f403940ca85461e07f Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 3 May 2025 15:47:38 +0530 Subject: [PATCH 1/3] feat: Update log display messages for clarity; enhance log navigation styles and layout --- app/src/components/footer/Footer.tsx | 2 +- app/src/components/ui/log/LogList.tsx | 50 ++++++++++++++---------- app/src/styles/components/logs/logs.scss | 47 +++++++++++++++------- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/app/src/components/footer/Footer.tsx b/app/src/components/footer/Footer.tsx index 8da3ba8..697c182 100644 --- a/app/src/components/footer/Footer.tsx +++ b/app/src/components/footer/Footer.tsx @@ -38,7 +38,7 @@ const Footer: React.FC = () => { {lastLog.message} ) : ( - "No logs yet." + "There are no logs to display at the moment." )} diff --git a/app/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx index ed86ba5..2ec50f8 100644 --- a/app/src/components/ui/log/LogList.tsx +++ b/app/src/components/ui/log/LogList.tsx @@ -23,6 +23,7 @@ const LogList: React.FC = () => { className="log-list-container" onClick={() => setIsLogListVisible(false)} > + {/* eslint-disable-next-line */}
{ @@ -46,32 +47,41 @@ const LogList: React.FC = () => {
{/* Tabs */} -
- {["all", "info", "warning", "error"].map((type) => ( - - ))} +
+
+ {["all", "info", "warning", "error"].map((type) => ( + + ))} +
+
{/* Log Entries */}
- {filteredLogs.map((log) => ( -
-
{GetLogIcon(log.type)}
-
-
{log.message}
-
- {formatTimestamp(log.timestamp)} + {filteredLogs.length > 0 ? ( + filteredLogs.map((log) => ( +
+
{GetLogIcon(log.type)}
+
+
{log.message}
+
+ {formatTimestamp(log.timestamp)} +
-
- ))} + )) + ) : ( +
There are no logs to display at the moment.
+ )}
diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/logs.scss index 9f85dd0..b7c1363 100644 --- a/app/src/styles/components/logs/logs.scss +++ b/app/src/styles/components/logs/logs.scss @@ -28,9 +28,9 @@ display: flex; align-items: center; gap: 6px; - .icon{ + .icon { @include flex-center; - scale: .8; + scale: 0.8; } } @@ -43,24 +43,38 @@ svg { scale: 1.6; } - &:hover{ + &:hover { background: var(--background-color); } } } + .log-nav-container { + @include flex-space-between; + align-items: flex-end; + .log-nav-wrapper { + display: flex; + gap: 6px; - .log-nav-wrapper { - display: flex; - gap: 6px; + .log-nav { + padding: 8px 16px; + border-radius: 19px; + } - .log-nav { - padding: 8px 16px; - border-radius: 19px; + .log-nav.active { + background-color: var(--background-color-accent); + color: var(--text-button-color); + } } - - .log-nav.active { - background-color: var(--background-color-accent); - color: var(--text-button-color); + .clear-button{ + margin: 0 6px; + padding: 4px 12px; + color: var(--text-disabled); + border-radius: #{$border-radius-large}; + &:hover{ + font-weight: 300; + color: var(--text-color); + background: var(--background-color-solid-gradient); + } } } @@ -98,7 +112,7 @@ text-wrap: nowrap; height: 100%; } - .log-entry-message{ + .log-entry-message { width: 100%; } } @@ -108,5 +122,10 @@ } } } + .no-log{ + padding: 20px; + text-align: center; + color: var(--text-color); + } } } From f8abd711160700885845a6b6aea040769134d4fd Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 3 May 2025 16:52:48 +0530 Subject: [PATCH 2/3] feat: Replace random ID generation with UUID for log entries; improve log entry consistency --- app/src/components/ui/log/LoggerContext.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/components/ui/log/LoggerContext.tsx b/app/src/components/ui/log/LoggerContext.tsx index 71bd467..f011609 100644 --- a/app/src/components/ui/log/LoggerContext.tsx +++ b/app/src/components/ui/log/LoggerContext.tsx @@ -1,4 +1,5 @@ import React, { createContext, useContext, useState, useCallback, useMemo } from "react"; +import { MathUtils } from "three"; export type LogType = "log" | "info" | "warning" | "error" | "success"; @@ -30,22 +31,17 @@ export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({ const [logs, setLogs] = useState([]); const [isLogListVisible, setIsLogListVisible] = useState(false); - const generateId = useCallback( - () => Math.random().toString(36).substring(2, 9), - [] - ); - const addLog = useCallback( (type: LogType, message: string) => { const newLog: LogEntry = { - id: generateId(), + id: MathUtils.generateUUID(), type, message, timestamp: new Date(), }; setLogs((prevLogs) => [...prevLogs, newLog]); }, - [generateId] + [] ); const loggerMethods: LoggerContextValue = useMemo(() => ({ From 2711702efae888419b944287e6141990fdc69ee2 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 3 May 2025 17:21:37 +0530 Subject: [PATCH 3/3] feat: Update icon components for consistency; refactor SVG attributes and styles --- app/src/components/icons/analysis.tsx | 36 +- .../ui/analysis/SemiCircleProgress.tsx | 26 +- .../events/points/creator/pointsCreator.tsx | 484 +++++++++--------- .../components/simulation/analysis.scss | 38 +- 4 files changed, 306 insertions(+), 278 deletions(-) diff --git a/app/src/components/icons/analysis.tsx b/app/src/components/icons/analysis.tsx index c387003..7c36f20 100644 --- a/app/src/components/icons/analysis.tsx +++ b/app/src/components/icons/analysis.tsx @@ -28,10 +28,10 @@ export function ProductionCapacityIcon() { xmlns="http://www.w3.org/2000/svg" > - + @@ -59,15 +59,11 @@ export function ROISummaryIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - + + - @@ -83,7 +79,7 @@ export function PowerIcon() { fill="none" xmlns="http://www.w3.org/2000/svg" > - + ); @@ -147,8 +143,8 @@ export function SonarCrownIcon() { ); @@ -166,9 +162,9 @@ export function CostBreakDownIcon() { ); diff --git a/app/src/components/ui/analysis/SemiCircleProgress.tsx b/app/src/components/ui/analysis/SemiCircleProgress.tsx index b2a3622..78422d3 100644 --- a/app/src/components/ui/analysis/SemiCircleProgress.tsx +++ b/app/src/components/ui/analysis/SemiCircleProgress.tsx @@ -1,9 +1,7 @@ -import React from "react"; - -const SemiCircleProgress = ({ progress = 10 }) => { +const SemiCircleProgress = ({ progress = 60, years = 4.02 }) => { const clampedProgress = Math.min(Math.max(progress, 0), 100); const radius = 80; - const strokeWidth = 20; + const strokeWidth = 26; const circumference = Math.PI * radius; const strokeDashoffset = circumference - (clampedProgress / 100) * circumference; @@ -15,22 +13,36 @@ const SemiCircleProgress = ({ progress = 10 }) => { {/* Progress track */} + + + + + + +
-
{clampedProgress}%
+
{years}
Years
diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 94128ce..1252446 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,250 +1,272 @@ import React, { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; -import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; +import useModuleStore, { + useSubModuleStore, +} from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { - useSelectedEventSphere, - useSelectedEventData, + useSelectedEventSphere, + useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore"; import { useThree } from "@react-three/fiber"; function PointsCreator() { - const { gl, raycaster, scene, pointer, camera } = useThree(); - const { subModule } = useSubModuleStore(); - const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); - const { activeModule } = useModuleStore(); - const transformRef = useRef(null); - const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); - const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); - const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); - const { selectedEventData, setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { subModule } = useSubModuleStore(); + const { events, updatePoint, getPointByUuid, getEventByModelUuid } = + useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(null); + const [transformMode, setTransformMode] = useState< + "translate" | "rotate" | null + >(null); + const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + const { + selectedEventSphere, + setSelectedEventSphere, + clearSelectedEventSphere, + } = useSelectedEventSphere(); + const { setSelectedEventData, clearSelectedEventData } = + useSelectedEventData(); - useEffect(() => { - if (selectedEventSphere) { - const eventData = getEventByModelUuid( - selectedEventSphere.userData.modelUuid - ); + useEffect(() => { + if (selectedEventSphere) { + const eventData = getEventByModelUuid( + selectedEventSphere.userData.modelUuid + ); - if (eventData) { - setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); - } else { - clearSelectedEventData(); - } - } else { - clearSelectedEventData(); - } - }, [selectedEventSphere]); + if (eventData) { + setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); + } else { + clearSelectedEventData(); + } + } else { + clearSelectedEventData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedEventSphere]); - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - const keyCombination = detectModifierKeys(e); - if (!selectedEventSphere) return; - if (keyCombination === "G") { - setTransformMode((prev) => (prev === "translate" ? null : "translate")); - } - if (keyCombination === "R") { - setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [selectedEventSphere]); - - const updatePointToState = (selectedEventSphere: THREE.Mesh) => { - let point = JSON.parse( - JSON.stringify(getPointByUuid(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)) - ); - if (point) { - point.position = [selectedEventSphere.position.x, selectedEventSphere.position.y, selectedEventSphere.position.z,]; - updatePoint(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid, point); - } + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedEventSphere) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } }; - useEffect(() => { - const canvasElement = gl.domElement; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedEventSphere]); - let drag = false; - let isMouseDown = false; - - const onMouseDown = () => { - isMouseDown = true; - drag = false; - }; - - const onMouseUp = () => { - if (selectedEventSphere && !drag) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - intersect.object.name === ('Event-Sphere') - ); - if (intersects.length === 0) { - clearSelectedEventSphere(); - setTransformMode(null); - } - } - } - - const onMouseMove = () => { - if (isMouseDown) { - drag = true; - } - }; - - if (subModule === 'mechanics') { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - }; - - }, [gl, subModule, selectedEventSphere]); - - return ( - <> - {activeModule === "simulation" && ( - <> - - {events.map((event, i) => { - if (event.type === "transfer") { - return ( - - {event.points.map((point) => ( - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - }} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: point.uuid, - }} - > - - - - ))} - - ); - } else if (event.type === "vehicle") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else if (event.type === "roboticArm") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else if (event.type === "machine") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else { - return null; - } - })} - - {selectedEventSphere && transformMode && ( - { - updatePointToState(selectedEventSphere); - }} - /> - )} - - )} - + const updatePointToState = (selectedEventSphere: THREE.Mesh) => { + let point = JSON.parse( + JSON.stringify( + getPointByUuid( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid + ) + ) ); + if (point) { + point.position = [ + selectedEventSphere.position.x, + selectedEventSphere.position.y, + selectedEventSphere.position.z, + ]; + updatePoint( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid, + point + ); + } + }; + + useEffect(() => { + const canvasElement = gl.domElement; + + let drag = false; + let isMouseDown = false; + + const onMouseDown = () => { + isMouseDown = true; + drag = false; + }; + + const onMouseUp = () => { + if (selectedEventSphere && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter((intersect) => intersect.object.name === "Event-Sphere"); + if (intersects.length === 0) { + clearSelectedEventSphere(); + setTransformMode(null); + } + } + }; + + const onMouseMove = () => { + if (isMouseDown) { + drag = true; + } + }; + + if (subModule === "mechanics") { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [gl, subModule, selectedEventSphere]); + + return ( + <> + {activeModule === "simulation" && ( + <> + + {events.map((event, index) => { + if (event.type === "transfer") { + return ( + + {event.points.map((point, i) => ( + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[point.uuid] + ); + }} + position={new THREE.Vector3(...point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: point.uuid, + }} + > + + + + ))} + + ); + } else if (event.type === "vehicle") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else if (event.type === "roboticArm") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else if (event.type === "machine") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else { + return null; + } + })} + + {selectedEventSphere && transformMode && ( + { + updatePointToState(selectedEventSphere); + }} + /> + )} + + )} + + ); } -export default PointsCreator; \ No newline at end of file +export default PointsCreator; diff --git a/app/src/styles/components/simulation/analysis.scss b/app/src/styles/components/simulation/analysis.scss index f33fb46..7cf906e 100644 --- a/app/src/styles/components/simulation/analysis.scss +++ b/app/src/styles/components/simulation/analysis.scss @@ -11,19 +11,12 @@ width: 100%; height: 100vh; pointer-events: none; - padding: 10px; z-index: 2; - .analysis-wrapper { - display: flex; - flex-direction: column; - gap: 12px; - } - .analysis-card { min-width: 333px; border-radius: 20px; - padding: 8px; + margin: 8px; pointer-events: all; .analysis-card-wrapper { @@ -50,6 +43,7 @@ } .sub-header { + font-weight: 300; font-size: var(--font-size-tiny); color: var(--text-button-color); } @@ -170,12 +164,13 @@ .footer-card { width: 100%; - background: var(--background-color-gray); + background: var(--background-color); border-radius: 6px; padding: 8px; display: flex; flex-direction: column; gap: 12px; + outline: 1px solid var(--border-color); &:first-child { width: 85%; @@ -326,10 +321,11 @@ flex-direction: column; gap: 3px; align-items: center; - + font-weight: 300; .key { - font-size: var(--font-size-xlarge); - color: #28B9F3; + font-weight: 500; + font-size: var(--font-size-large); + color: #28b9f3; } } } @@ -376,8 +372,9 @@ gap: 6px; .metric-item { + padding: 8px; border-radius: #{$border-radius-large}; - background-color: var(--background-color); + background: var(--background-color); border: 1px solid var(--border-color); } } @@ -385,10 +382,12 @@ } .cost-breakdown { - background-color: var(--background-color); + background: var(--background-color); border: 1px solid var(--border-color); - border-radius: #{$border-radius-extra-large}; + border-radius: #{$border-radius-large}; + max-height: 20vh; padding: 16px; + overflow: auto; .breakdown-header { display: flex; @@ -448,7 +447,7 @@ } .tips-section { - background-color: var(--background-color); + background: var(--background-color); border-radius: #{$border-radius-large}; outline: 1px solid var(--border-color); display: flex; @@ -510,18 +509,17 @@ .label-wrapper { width: 100%; position: absolute; - bottom: 0px; + bottom: 2px; display: flex; flex-direction: column; justify-content: center; align-items: center; .label { - font-size: var(--font-size-xlarge); + font-size: var(--font-size-xxlarge); } } } - } .breakdown-table-wrapper { @@ -547,4 +545,4 @@ } } -// Breakdown Table Open/Close Logic \ No newline at end of file +// Breakdown Table Open/Close Logic