diff --git a/app/src/app.tsx b/app/src/app.tsx index f5e19b0..0b6fd3e 100644 --- a/app/src/app.tsx +++ b/app/src/app.tsx @@ -3,23 +3,20 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Dashboard from "./pages/Dashboard"; import Project from "./pages/Project"; import UserAuth from "./pages/UserAuth"; -import ToastProvider from "./components/templates/ToastProvider"; -import "./styles/main.scss" +import "./styles/main.scss"; +import { LoggerProvider } from "./components/ui/log/LoggerContext"; const App: React.FC = () => { return ( - + - } - /> + } /> } /> } /> - + ); }; diff --git a/app/src/components/footer/Footer.tsx b/app/src/components/footer/Footer.tsx new file mode 100644 index 0000000..19fc6c5 --- /dev/null +++ b/app/src/components/footer/Footer.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { HelpIcon } from "../icons/DashboardIcon"; +import { + LogInfoIcon, + ErrorIcon, + WarningIcon, +} from "../icons/ExportCommonIcons"; // Adjust path as needed +import { useLogger } from "../ui/log/LoggerContext"; + +const getLogIcon = (type: string) => { + switch (type) { + case "info": + return ; + case "error": + return ; + case "warning": + return ; + case "log": + default: + return ; + } +}; + +const Footer: React.FC = () => { + const { logs, setIsLogListVisible } = useLogger(); + const lastLog = logs.length > 0 ? logs[logs.length - 1] : null; + + return ( +
+
+
+
+
Selection
+
+
+
+
Rotate/Zoom
+
+
+
+
Pan/Context Menu
+
+
+ +
+
setIsLogListVisible(true)}> + {lastLog ? ( + <> + {getLogIcon(lastLog.type)} + {lastLog.message} + + ) : ( + "No logs yet." + )} +
+
+ V 0.01 +
+ +
+
+
+
+ ); +}; + +export default Footer; diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index eaa7701..4137498 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -458,7 +458,7 @@ export function InfoIcon() { > { ); }; + +export const LogListIcon = () => { + return ( + + + + ); +}; + +export const LogTickIcon = () => { + return ( + + + + + + + + + + + ); +}; + +export const LogInfoIcon = () => { + return ( + + + + + + + ); +}; + +export const WarningIcon = () => { + return ( + + + + + + + ); +}; + +export const ErrorIcon = () => { + return ( + + + + ); +}; diff --git a/app/src/components/icons/SimulationIcons.tsx b/app/src/components/icons/SimulationIcons.tsx index ef23c8f..1e89a51 100644 --- a/app/src/components/icons/SimulationIcons.tsx +++ b/app/src/components/icons/SimulationIcons.tsx @@ -125,21 +125,21 @@ export function ResetIcon() { > @@ -158,7 +158,7 @@ export function PlayStopIcon() { > @@ -177,7 +177,7 @@ export function ExitIcon() { > diff --git a/app/src/components/layout/sidebarLeft/Header.tsx b/app/src/components/layout/sidebarLeft/Header.tsx index 06706c6..0cac495 100644 --- a/app/src/components/layout/sidebarLeft/Header.tsx +++ b/app/src/components/layout/sidebarLeft/Header.tsx @@ -19,7 +19,7 @@ const Header: React.FC = () => { -
{ if (activeModule !== "market") { @@ -29,7 +29,7 @@ const Header: React.FC = () => { }} > -
+ ); }; diff --git a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx index 9d2889f..9b16186 100644 --- a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx +++ b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { AI_Icon } from "../../../icons/ExportCommonIcons"; +import { AIIcon } from "../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../ui/inputs/RegularDropDown"; import { AnalysisPresetsType } from "../../../../types/analysis"; import RenderAnalysisInputs from "./RenderAnalysisInputs"; @@ -13,53 +13,24 @@ const Analysis: React.FC = () => { const AnalysisPresets: AnalysisPresetsType = { "Throughput time": [ - { type: "default", inputs: { label: "Height", activeOption: "mm" } }, - { type: "default", inputs: { label: "Width", activeOption: "mm" } }, - { type: "default", inputs: { label: "Length", activeOption: "mtr" } }, - { type: "default", inputs: { label: "Thickness", activeOption: "mm" } }, - { - type: "default", - inputs: { label: "Raw Material", activeOption: "mm" }, - }, - { - type: "range", - inputs: { label: "Material flow", activeOption: "m/min" }, - }, + { type: "default", inputs: { label: "Cycle time", activeOption: "s" } }, + { type: "default", inputs: { label: "machines / lines", activeOption: "item" } }, + { type: "default", inputs: { label: "Machine uptime", activeOption: "%" } }, ], "Production capacity": [ - { type: "default", inputs: { label: "Height", activeOption: "mm" } }, - { type: "default", inputs: { label: "Width", activeOption: "mm" } }, - { type: "default", inputs: { label: "Length", activeOption: "mtr" } }, - { type: "default", inputs: { label: "Thickness", activeOption: "mm" } }, - { - type: "default", - inputs: { label: "Raw Material", activeOption: "mm" }, - }, - { - type: "range", - inputs: { label: "Material flow", activeOption: "m/min" }, - }, - { - type: "range", - inputs: { label: "Shift 1", activeOption: "hr(s)" }, - }, - { - type: "range", - inputs: { label: "Shift 2", activeOption: "hr(s)" }, - }, - { - type: "range", - inputs: { label: "Over time", activeOption: "hr(s)" }, - }, + { type: "default", inputs: { label: "Shift length", activeOption: "hr" } }, + { type: "default", inputs: { label: "Shifts / day", activeOption: "unit" } }, + { type: "default", inputs: { label: "Working days / year", activeOption: "days" } }, + { type: "default", inputs: { label: "Yield rate", activeOption: "%" } }, ], ROI: [ { type: "default", - inputs: { label: "Equipment Cost", activeOption: "INR" }, + inputs: { label: "Selling price", activeOption: "INR" }, }, { type: "default", - inputs: { label: "Employee Salary", activeOption: "INR" }, + inputs: { label: "Material cost", activeOption: "INR" }, }, { type: "default", @@ -67,7 +38,7 @@ const Analysis: React.FC = () => { }, { type: "default", - inputs: { label: "Cost per unit", activeOption: "INR" }, + inputs: { label: "Maintenance cost", activeOption: "INR" }, }, { type: "default", @@ -75,20 +46,19 @@ const Analysis: React.FC = () => { }, { type: "default", - inputs: { label: "Upkeep Cost", activeOption: "INR" }, + inputs: { label: "Fixed costs", activeOption: "INR" }, }, { type: "default", - inputs: { label: "Working Hours", activeOption: "Hrs" }, + inputs: { label: "Salvage value", activeOption: "Hrs" }, }, { type: "default", - inputs: { label: "Power Usage", activeOption: "KWH" }, + inputs: { label: "Production period", activeOption: "yrs" }, }, - { type: "default", inputs: { label: "KWH", activeOption: "Mos" } }, { type: "default", - inputs: { label: "Man Power", activeOption: "Person" }, + inputs: { label: "Tax rate", activeOption: "%" }, }, ], }; @@ -98,7 +68,7 @@ const Analysis: React.FC = () => {
Object
- Generate Report + Generate Report
@@ -121,7 +91,7 @@ const Analysis: React.FC = () => { />
- +
Create Custom Analysis
diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 8b91afc..514808a 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import InputRange from "../../../ui/inputs/InputRange"; import InputToggle from "../../../ui/inputs/InputToggle"; -import { AI_Icon } from "../../../icons/ExportCommonIcons"; +import { AIIcon } from "../../../icons/ExportCommonIcons"; import LabeledButton from "../../../ui/inputs/LabledButton"; import { useAzimuth, @@ -225,7 +225,7 @@ const GlobalProperties: React.FC = () => {
Environment
- + Optimize
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx index 4fa9105..053fc14 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from "react"; import { - useSelectedEventData, - useSelectedEventSphere, - useSelectedProduct, + useSelectedEventData, + useSelectedEventSphere, + useSelectedProduct, } from "../../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; import ConveyorMechanics from "./mechanics/conveyorMechanics"; @@ -15,114 +15,118 @@ import { handleAddEventToProduct } from "../../../../../modules/simulation/event import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; const EventProperties: React.FC = () => { - const { selectedEventData } = useSelectedEventData(); - const { getEventByModelUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); - const [currentEventData, setCurrentEventData] = useState(null); - const [assetType, setAssetType] = useState(null); - const { products, addEvent } = useProductStore(); - const { selectedEventSphere } = useSelectedEventSphere(); + const { selectedEventData } = useSelectedEventData(); + const { getEventByModelUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const [currentEventData, setCurrentEventData] = useState( + null + ); + const [assetType, setAssetType] = useState(null); + const { products, addEvent } = useProductStore(); + const { selectedEventSphere } = useSelectedEventSphere(); - useEffect(() => { - const event = getCurrentEventData(); - setCurrentEventData(event); + useEffect(() => { + const event = getCurrentEventData(); + setCurrentEventData(event); - const type = determineAssetType(event); - setAssetType(type); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedEventData, selectedProduct]); - - const getCurrentEventData = () => { - if (!selectedEventData?.data || !selectedProduct) return null; - return ( - getEventByModelUuid( - selectedProduct.productId, - selectedEventData.data.modelUuid - ) ?? null - ); - }; - - const determineAssetType = (event: EventsSchema | null) => { - if (!event) return null; - - switch (event.type) { - case "transfer": - return "conveyor"; - case "vehicle": - return "vehicle"; - case "roboticArm": - return "roboticArm"; - case "machine": - return "machine"; - case "storageUnit": - return "storageUnit"; - default: - return null; - } - }; + const type = determineAssetType(event); + setAssetType(type); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedEventData, selectedProduct]); + const getCurrentEventData = () => { + if (!selectedEventData?.data || !selectedProduct) return null; return ( -
- {currentEventData && ( - <> -
-
- {selectedEventData?.data.modelName} -
-
- {assetType === "conveyor" && } - {assetType === "vehicle" && } - {assetType === "roboticArm" && } - {assetType === "machine" && } - {assetType === "storageUnit" && } - - )} - {!currentEventData && selectedEventSphere && ( -
-

- Oops! It looks like this object doesn't have an - event assigned yet. To continue, please link it to one of the - products below. -

- -
-

- Here are some products you can add it to: -

-
    - {products.map((product) => ( -
  • - -
  • - ))} -
-
-
- ) - } - {!selectedEventSphere && ( -
-

- Oops! It looks like you haven't selected an event - point yet. Please select an event to view its properties. -

-
- )} -
+ getEventByModelUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid + ) ?? null ); + }; + + const determineAssetType = (event: EventsSchema | null) => { + if (!event) return null; + + switch (event.type) { + case "transfer": + return "conveyor"; + case "vehicle": + return "vehicle"; + case "roboticArm": + return "roboticArm"; + case "machine": + return "machine"; + case "storageUnit": + return "storageUnit"; + default: + return null; + } + }; + + return ( +
+ {currentEventData && ( + <> +
+
+ {selectedEventData?.data.modelName} +
+
+ {assetType === "conveyor" && } + {assetType === "vehicle" && } + {assetType === "roboticArm" && } + {assetType === "machine" && } + {assetType === "storageUnit" && } + + )} + {!currentEventData && selectedEventSphere && ( +
+

+ Oops! It looks like this object doesn't have an + event assigned yet. To continue, please link it to one of the + products below. +

+ +
+

+ Here are some products you can add it to: +

+
+ {products.map((product) => ( + + ))} +
+
+
+ )} + {!selectedEventSphere && ( +
+

+ Oops! It looks like you haven't selected an event + point yet. Please select an event to view its properties. +

+
+ )} +
+ ); }; export default EventProperties; diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 21ea6a7..14bc517 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -36,7 +36,6 @@ import { import useToggleStore from "../../store/useUIToggleStore"; import { use3DWidget, - useDroppedObjectsStore, useFloatingWidget, } from "../../store/visualization/useDroppedObjectsStore"; @@ -57,20 +56,18 @@ const Tools: React.FC = () => { const { widgets3D } = use3DWidget(); - const zones = useDroppedObjectsStore((state) => state.zones); - // wall options const { toggleView, setToggleView } = useToggleView(); const { setDeleteTool } = useDeleteTool(); const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); - const { transformMode, setTransformMode } = useTransformMode(); - const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); - const { movePoint, setMovePoint } = useMovePoint(); - const { toolMode, setToolMode } = useToolMode(); + const { setTransformMode } = useTransformMode(); + const { setDeletePointOrLine } = useDeletePointOrLine(); + const { setMovePoint } = useMovePoint(); + const { setToolMode } = useToolMode(); const { activeTool, setActiveTool } = useActiveTool(); - const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); + const { setRefTextUpdate } = useRefTextUpdate(); // Reset activeTool whenever activeModule changes useEffect(() => { @@ -209,268 +206,266 @@ const Tools: React.FC = () => { return ( <> {!isPlaying ? ( - <> -
-
-
- {activeSubTool == "cursor" && ( -
{ - setActiveTool("cursor"); - }} - > - -
- )} - {activeSubTool == "free-hand" && ( -
{ - setActiveTool("free-hand"); - }} - > - -
- )} - {activeSubTool == "delete" && ( -
{ - setActiveTool("delete"); - }} - > - -
- )} - {activeModule !== "visualization" && ( -
{ - setOpenDrop(!openDrop); - }} - > - - {openDrop && ( -
-
{ - setOpenDrop(false); - setActiveTool("cursor"); - setActiveSubTool("cursor"); - }} - > -
- {activeSubTool === "cursor" && } -
- -
Cursor
-
-
{ - setOpenDrop(false); - setActiveTool("free-hand"); - setActiveSubTool("free-hand"); - }} - > -
- {activeSubTool === "free-hand" && } -
- -
Free Hand
-
-
{ - setOpenDrop(false); - setActiveTool("delete"); - setActiveSubTool("delete"); - }} - > -
- {activeSubTool === "delete" && } -
- -
Delete
-
-
- )} -
- )} -
-
- {!toggleThreeD && activeModule === "builder" && ( - <> -
-
-
{ - setActiveTool("draw-wall"); - }} - title="Wall" - > - -
-
{ - setActiveTool("draw-zone"); - }} - title="Zone" - > - -
-
{ - setActiveTool("draw-aisle"); - }} - title="Aisle" - > - -
-
{ - setActiveTool("draw-floor"); - }} - title="Floor" - > - -
-
- - )} - {activeModule === "builder" && ( - <> -
-
-
{ - setActiveTool("measure"); - }} - title="Measure" - > - -
-
- - )} - {activeModule === "simulation" && ( - <> -
-
-
{ - setActiveTool("pen"); - }} - > - -
-
- - )} - {activeModule === "visualization" && ( - <> -
-
-
{ - handleSaveTemplate({ - addTemplate, - floatingWidget, - widgets3D, - selectedZone, - templates, - visualizationSocket, - }); - }} - > - -
-
- - )} -
-
-
{ - setActiveTool("comment"); - }} - > - -
- {toggleThreeD && ( +
+
+
+ {activeSubTool == "cursor" && (
{ - setIsPlaying(!isPlaying); + setActiveTool("cursor"); }} > - + +
+ )} + {activeSubTool == "free-hand" && ( +
{ + setActiveTool("free-hand"); + }} + > + +
+ )} + {activeSubTool == "delete" && ( +
{ + setActiveTool("delete"); + }} + > + +
+ )} + {activeModule !== "visualization" && ( +
{ + setOpenDrop(!openDrop); + }} + > + + {openDrop && ( +
+
{ + setOpenDrop(false); + setActiveTool("cursor"); + setActiveSubTool("cursor"); + }} + > +
+ {activeSubTool === "cursor" && } +
+ +
Cursor
+
+
{ + setOpenDrop(false); + setActiveTool("free-hand"); + setActiveSubTool("free-hand"); + }} + > +
+ {activeSubTool === "free-hand" && } +
+ +
Free Hand
+
+
{ + setOpenDrop(false); + setActiveTool("delete"); + setActiveSubTool("delete"); + }} + > +
+ {activeSubTool === "delete" && } +
+ +
Delete
+
+
+ )}
)}
- {activeModule === "builder" && ( - <> -
+
+ {!toggleThreeD && activeModule === "builder" && ( + <> +
+
{ + setActiveTool("draw-wall"); + }} + title="Wall" > -
- 2d -
-
- 3d -
+
- +
{ + setActiveTool("draw-zone"); + }} + title="Zone" + > + +
+
{ + setActiveTool("draw-aisle"); + }} + title="Aisle" + > + +
+
{ + setActiveTool("draw-floor"); + }} + title="Floor" + > + +
+
+ + )} + {activeModule === "builder" && ( + <> +
+
+
{ + setActiveTool("measure"); + }} + title="Measure" + > + +
+
+ + )} + {activeModule === "simulation" && ( + <> +
+
+
{ + setActiveTool("pen"); + }} + > + +
+
+ + )} + {activeModule === "visualization" && ( + <> +
+
+
{ + handleSaveTemplate({ + addTemplate, + floatingWidget, + widgets3D, + selectedZone, + templates, + visualizationSocket, + }); + }} + > + +
+
+ + )} +
+
+
{ + setActiveTool("comment"); + }} + > + +
+ {toggleThreeD && ( +
{ + setIsPlaying(!isPlaying); + }} + > + +
)}
- + {activeModule === "builder" && ( + <> +
+
+
+ 2d +
+
+ 3d +
+
+ + )} +
) : ( <> {activeModule !== "simulation" && ( -
setIsPlaying(false)}> +
+ )} )} diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx index 11101fd..63ae707 100644 --- a/app/src/components/ui/analysis/ROISummary.tsx +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { ROISummaryIcon } from "../../icons/analysis"; import SemiCircleProgress from "./SemiCircleProgress"; +import { ArrowIcon } from "../../icons/ExportCommonIcons"; const ROISummary = ({ roiSummaryData = { @@ -121,7 +122,7 @@ const ROISummary = ({
- {isTableOpen ? "⌵" : "⌵"} +
{ + const { logs, clear, setIsLogListVisible } = useLogger(); + const [selectedTab, setSelectedTab] = useState< + "all" | "info" | "warning" | "error" | "log" + >("all"); + + const getLogIcon = (type: string) => { + switch (type) { + case "info": + return ; + case "error": + return ; + case "warning": + return ; + case "log": + default: + return ; + } + }; + + const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString(); + + const filteredLogs = + selectedTab === "all" + ? [...logs].reverse() + : [...logs].filter((log) => log.type === selectedTab).reverse(); + + return ( + // eslint-disable-next-line +
setIsLogListVisible(false)} + > +
{ + e.stopPropagation(); + }} + > +
+
+
+ +
+
Log List
+
+ +
+ + {/* Tabs */} +
+ {["all", "info", "warning", "error"].map((type) => ( +
setSelectedTab(type as any)} + > + {`${type.charAt(0).toUpperCase() + type.slice(1)} Logs`} +
+ ))} +
+ + {/* Log Entries */} +
+ {filteredLogs.map((log) => ( +
+
{getLogIcon(log.type)}
+
+
{log.message}
+
+ {formatTimestamp(log.timestamp)} +
+
+
+ ))} +
+
+
+ ); +}; + +export default LogList; diff --git a/app/src/components/ui/log/LoggerContext.tsx b/app/src/components/ui/log/LoggerContext.tsx new file mode 100644 index 0000000..f323b93 --- /dev/null +++ b/app/src/components/ui/log/LoggerContext.tsx @@ -0,0 +1,77 @@ +import React, { createContext, useContext, useState, useCallback } from "react"; + +export type LogType = "log" | "info" | "warning" | "error"; + +export interface LogEntry { + id: string; + type: LogType; + message: string; + timestamp: Date; +} + +interface LoggerContextValue { + logs: LogEntry[]; + setLogs: React.Dispatch>; + isLogListVisible: boolean; + setIsLogListVisible: React.Dispatch>; + log: (message: string) => void; + info: (message: string) => void; + warn: (message: string) => void; + error: (message: string) => void; + clear: () => void; +} + +const LoggerContext = createContext(undefined); + +export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + 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(), + type, + message, + timestamp: new Date(), + }; + setLogs((prevLogs) => [...prevLogs, newLog]); + }, + [generateId] + ); + + const loggerMethods: LoggerContextValue = { + logs, + setLogs, + isLogListVisible, + setIsLogListVisible, + log: (message: string) => addLog("log", message), + info: (message: string) => addLog("info", message), + warn: (message: string) => addLog("warning", message), + error: (message: string) => addLog("error", message), + clear: () => { + setLogs([]); + }, + }; + + return ( + + {children} + + ); +}; + +export const useLogger = () => { + const context = useContext(LoggerContext); + if (!context) { + throw new Error("useLogger must be used within a LoggerProvider"); + } + return context; +}; diff --git a/app/src/components/ui/log/logger.ts b/app/src/components/ui/log/logger.ts new file mode 100644 index 0000000..1781e26 --- /dev/null +++ b/app/src/components/ui/log/logger.ts @@ -0,0 +1,71 @@ +type LogType = 'log' | 'info' | 'warning' | 'error'; + +interface LogEntry { + type: LogType; + message: string; + timestamp: Date; + context?: string; +} + +class Logger { + private static instance: Logger; + private logs: LogEntry[] = []; + private subscribers: Array<(log: LogEntry) => void> = []; + + private constructor() {} + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + private notifySubscribers(log: LogEntry) { + this.subscribers.forEach(callback => callback(log)); + } + + private addLog(type: LogType, message: string, context?: string) { + const log: LogEntry = { type, message, timestamp: new Date(), context }; + this.logs.push(log); + this.notifySubscribers(log); + + if (process.env.NODE_ENV === 'development') { + const logMessage = context ? `[${context}] ${message}` : message; + console[type === 'warning' ? 'warn' : type](logMessage); + } + } + + public log(message: string, context?: string) { + this.addLog('log', message, context); + } + + public info(message: string, context?: string) { + this.addLog('info', message, context); + } + + public warning(message: string, context?: string) { + this.addLog('warning', message, context); + } + + public error(message: string, context?: string) { + this.addLog('error', message, context); + } + + public getLogs(): LogEntry[] { + return [...this.logs]; + } + + public clear() { + this.logs = []; + } + + public subscribe(callback: (log: LogEntry) => void) { + this.subscribers.push(callback); + return () => { + this.subscribers = this.subscribers.filter(sub => sub !== callback); + }; + } +} + +export const logger = Logger.getInstance(); diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index 4c7c76d..e569250 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -12,25 +12,31 @@ import { EndIcon, ExpandIcon, HourlySimulationIcon, + InfoIcon, MonthlyROI, SpeedIcon, StartIcon, } from "../../icons/ExportCommonIcons"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; +import { useSubModuleStore } from "../../../store/useModuleStore"; +import ProductionCapacity from "../analysis/ProductionCapacity"; +import ThroughputSummary from "../analysis/ThroughputSummary"; +import ROISummary from "../analysis/ROISummary"; const SimulationPlayer: React.FC = () => { const MAX_SPEED = 8; // Maximum speed + const isDragging = useRef(false); + const sliderRef = useRef(null); const [expand, setExpand] = useState(true); + const [playSimulation, setPlaySimulation] = useState(false); const { speed, setSpeed } = useAnimationPlaySpeed(); - const [playSimulation, setPlaySimulation] = useState(false); const { setIsPlaying } = usePlayButtonStore(); - const sliderRef = useRef(null); - const isDragging = useRef(false); const { setActiveTool } = useActiveTool(); const { isPaused, setIsPaused } = usePauseButtonStore(); const { isReset, setReset } = useResetButtonStore(); + const { subModule } = useSubModuleStore(); // Button functions const handleReset = () => { @@ -114,7 +120,7 @@ const SimulationPlayer: React.FC = () => { }; // Store colors for each process item - const [processColors, setProcessColors] = useState([]); + const [_, setProcessColors] = useState([]); // Generate colors on mount or when process changes useEffect(() => { @@ -159,210 +165,236 @@ const SimulationPlayer: React.FC = () => { }; return ( -
-
-
-
- {/* hourlySimulation */} -
-
-
- + <> +
+
+
+ {subModule === "analysis" && ( +
+ {/* hourlySimulation */} +
+
+
+ +
+
Hourly Simulation
+
+
+
+
-
Hourly Simulation
-
-
-
-
-
- {/* dailyProduction */} -
-
-
- + {/* dailyProduction */} +
+
+
+ +
+
Daily Production
+
+
+
+
-
Daily Production
-
-
-
-
-
- {/* monthlyROI */} -
-
-
- + {/* monthlyROI */} +
+
+
+ +
+
Monthly ROI
+
+
+
+
{" "}
-
Monthly ROI
-
-
-
{" "} -
-
-
- - - - -
-
-
-
-
-
- + )} + {subModule === "simulations" && ( +
+ + {playSimulation + ? "Paused - system idle." + : "Running simulation..."}
-
-
23 April ,25
-
04:41 PM
-
-
-
-
- {intervals.map((label, index) => { - const segmentProgress = (index / totalSegments) * 100; - const isFilled = progress >= segmentProgress; - return ( - -
-
{label} mins
-
-
- {index < intervals.length - 1 && ( -
= ((index + 1) / totalSegments) * 100 - ? "filled" - : "" - }`} - >
- )} -
- ); - })} -
-
- -
-
-
00:10:20
-
-
- -
-
-
-
-
-
- -
- Speed -
-
-
0.5X
-
-
-
-
-
-
-
-
-
-
+ )} +
+ + + + {subModule === "analysis" && ( - -
-
4x
+ )}
-
-
-
00:00
-
24:00
-
-
- {process.map((item, index) => ( -
-
+
+
+
+
+
- ))} +
+
23 April ,25
+
04:41 PM
+
+
+
+
+ {intervals.map((label, index) => { + const segmentProgress = (index / totalSegments) * 100; + const isFilled = progress >= segmentProgress; + return ( + +
+
{label} mins
+
+
+ {index < intervals.length - 1 && ( +
= ((index + 1) / totalSegments) * 100 + ? "filled" + : "" + }`} + >
+ )} +
+ ); + })} +
+
+ +
+
+
00:10:20
+
+
+ +
+
+
+
+
+
+ +
+ Speed +
+
+
0.5X
+
+
+
+
+
+
+
+
+
+
+ + +
+
4x
+
+ {subModule === "analysis" && ( +
+
00:00
+
24:00
+
+
+ {process.map((item, index) => ( +
+
+
+ ))} +
+
+
+ )}
-
+
+
+ + +
+ +
+ ); }; diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index 56e3054..a8f44b4 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -142,109 +142,109 @@ const RealTimeVisulization: React.FC = () => { }); }, [selectedZone]); - const handleDrop = async (event: React.DragEvent) => { - event.preventDefault(); - try { - const email = localStorage.getItem("email") ?? ""; - const organization = email?.split("@")[1]?.split(".")[0]; + // const handleDrop = async (event: React.DragEvent) => { + // event.preventDefault(); + // try { + // const email = localStorage.getItem("email") ?? ""; + // const organization = email?.split("@")[1]?.split(".")[0]; - const data = event.dataTransfer.getData("text/plain"); - if (widgetSubOption === "3D") return; - if (!data || selectedZone.zoneName === "") return; + // const data = event.dataTransfer.getData("text/plain"); + // if (widgetSubOption === "3D") return; + // if (!data || selectedZone.zoneName === "") return; - const droppedData = JSON.parse(data); - const canvasElement = document.getElementById("real-time-vis-canvas"); - if (!canvasElement) throw new Error("Canvas element not found"); + // const droppedData = JSON.parse(data); + // const canvasElement = document.getElementById("real-time-vis-canvas"); + // if (!canvasElement) throw new Error("Canvas element not found"); - const rect = canvasElement.getBoundingClientRect(); - const relativeX = event.clientX - rect.left; - const relativeY = event.clientY - rect.top; + // const rect = canvasElement.getBoundingClientRect(); + // const relativeX = event.clientX - rect.left; + // const relativeY = event.clientY - rect.top; - // Widget dimensions - const widgetWidth = droppedData.width ?? 125; - const widgetHeight = droppedData.height ?? 100; + // // Widget dimensions + // const widgetWidth = droppedData.width ?? 125; + // const widgetHeight = droppedData.height ?? 100; - // Center the widget at cursor - const centerOffsetX = widgetWidth / 2; - const centerOffsetY = widgetHeight / 2; + // // Center the widget at cursor + // const centerOffsetX = widgetWidth / 2; + // const centerOffsetY = widgetHeight / 2; - const adjustedX = relativeX - centerOffsetX; - const adjustedY = relativeY - centerOffsetY; + // const adjustedX = relativeX - centerOffsetX; + // const adjustedY = relativeY - centerOffsetY; - const finalPosition = determinePosition(rect, adjustedX, adjustedY); - const [activeProp1, activeProp2] = getActiveProperties(finalPosition); + // const finalPosition = determinePosition(rect, adjustedX, adjustedY); + // const [activeProp1, activeProp2] = getActiveProperties(finalPosition); - let finalY = 0; - let finalX = 0; + // let finalY = 0; + // let finalX = 0; - if (activeProp1 === "top") { - finalY = adjustedY; - } else { - finalY = rect.height - (adjustedY + widgetHeight); - } + // if (activeProp1 === "top") { + // finalY = adjustedY; + // } else { + // finalY = rect.height - (adjustedY + widgetHeight); + // } - if (activeProp2 === "left") { - finalX = adjustedX; - } else { - finalX = rect.width - (adjustedX + widgetWidth); - } + // if (activeProp2 === "left") { + // finalX = adjustedX; + // } else { + // finalX = rect.width - (adjustedX + widgetWidth); + // } - // Clamp to boundaries - finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX)); - finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY)); + // // Clamp to boundaries + // finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX)); + // finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY)); - const boundedPosition = { - ...finalPosition, - [activeProp1]: finalY, - [activeProp2]: finalX, - [activeProp1 === "top" ? "bottom" : "top"]: "auto", - [activeProp2 === "left" ? "right" : "left"]: "auto", - }; + // const boundedPosition = { + // ...finalPosition, + // [activeProp1]: finalY, + // [activeProp2]: finalX, + // [activeProp1 === "top" ? "bottom" : "top"]: "auto", + // [activeProp2 === "left" ? "right" : "left"]: "auto", + // }; - const newObject = { - ...droppedData, - id: generateUniqueId(), - position: boundedPosition, - }; + // const newObject = { + // ...droppedData, + // id: generateUniqueId(), + // position: boundedPosition, + // }; - const existingZone = - useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; - if (!existingZone) { - useDroppedObjectsStore - .getState() - .setZone(selectedZone.zoneName, selectedZone.zoneId); - } + // const existingZone = + // useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; + // if (!existingZone) { + // useDroppedObjectsStore + // .getState() + // .setZone(selectedZone.zoneName, selectedZone.zoneId); + // } - const addFloatingWidget = { - organization, - widget: newObject, - zoneId: selectedZone.zoneId, - }; + // const addFloatingWidget = { + // organization, + // widget: newObject, + // zoneId: selectedZone.zoneId, + // }; - if (visualizationSocket) { - visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); - } + // if (visualizationSocket) { + // visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); + // } - useDroppedObjectsStore - .getState() - .addObject(selectedZone.zoneName, newObject); + // useDroppedObjectsStore + // .getState() + // .addObject(selectedZone.zoneName, newObject); - const droppedObjectsStore = useDroppedObjectsStore.getState(); - const currentZone = droppedObjectsStore.zones[selectedZone.zoneName]; + // const droppedObjectsStore = useDroppedObjectsStore.getState(); + // const currentZone = droppedObjectsStore.zones[selectedZone.zoneName]; - if (currentZone && currentZone.zoneId === selectedZone.zoneId) { - console.log( - `Objects for Zone ${selectedZone.zoneId}:`, - currentZone.objects - ); - setFloatingWidget(currentZone.objects); - } else { - console.warn("Zone not found or zoneId mismatch"); - } - } catch (error) { - console.error("Error in handleDrop:", error); - } - }; + // if (currentZone && currentZone.zoneId === selectedZone.zoneId) { + // console.log( + // `Objects for Zone ${selectedZone.zoneId}:`, + // currentZone.objects + // ); + // setFloatingWidget(currentZone.objects); + // } else { + // console.warn("Zone not found or zoneId mismatch"); + // } + // } catch (error) { + // console.error("Error in handleDrop:", error); + // } + // }; useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -301,16 +301,7 @@ const RealTimeVisulization: React.FC = () => { } `} -
+
{openConfirmationPopup && ( @@ -321,20 +312,6 @@ const RealTimeVisulization: React.FC = () => { /> )} -
handleDrop(event)} - onDragOver={(event) => event.preventDefault()} - > - -
{activeModule === "visualization" && selectedZone.zoneName !== "" && ( )} diff --git a/app/src/modules/visualization/functions/handleUiDrop.ts b/app/src/modules/visualization/functions/handleUiDrop.ts new file mode 100644 index 0000000..a70742e --- /dev/null +++ b/app/src/modules/visualization/functions/handleUiDrop.ts @@ -0,0 +1,122 @@ +import { generateUniqueId } from "../../../functions/generateUniqueId"; +import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore"; +import { determinePosition } from "./determinePosition"; +import { getActiveProperties } from "./getActiveProperties"; + +interface HandleDropProps { + widgetSubOption: any; + visualizationSocket: any; + selectedZone: any; + setFloatingWidget: (value: any) => void; + event: React.DragEvent +} + +export const createHandleDrop = ({ + widgetSubOption, + visualizationSocket, + selectedZone, + setFloatingWidget, + event, +}: HandleDropProps) => { + event.preventDefault(); + try { + const email = localStorage.getItem("email") ?? ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + const data = event.dataTransfer.getData("text/plain"); + if (widgetSubOption === "3D") return; + if (!data || selectedZone.zoneName === "") return; + + const droppedData = JSON.parse(data); + const canvasElement = document.getElementById("real-time-vis-canvas"); + if (!canvasElement) throw new Error("Canvas element not found"); + + const rect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - rect.left; + const relativeY = event.clientY - rect.top; + + // Widget dimensions + const widgetWidth = droppedData.width ?? 125; + const widgetHeight = droppedData.height ?? 100; + + // Center the widget at cursor + const centerOffsetX = widgetWidth / 2; + const centerOffsetY = widgetHeight / 2; + + const adjustedX = relativeX - centerOffsetX; + const adjustedY = relativeY - centerOffsetY; + + const finalPosition = determinePosition(rect, adjustedX, adjustedY); + const [activeProp1, activeProp2] = getActiveProperties(finalPosition); + + let finalY = 0; + let finalX = 0; + + if (activeProp1 === "top") { + finalY = adjustedY; + } else { + finalY = rect.height - (adjustedY + widgetHeight); + } + + if (activeProp2 === "left") { + finalX = adjustedX; + } else { + finalX = rect.width - (adjustedX + widgetWidth); + } + + // Clamp to boundaries + finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX)); + finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY)); + + const boundedPosition = { + ...finalPosition, + [activeProp1]: finalY, + [activeProp2]: finalX, + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", + }; + + const newObject = { + ...droppedData, + id: generateUniqueId(), + position: boundedPosition, + }; + + const existingZone = + useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; + if (!existingZone) { + useDroppedObjectsStore + .getState() + .setZone(selectedZone.zoneName, selectedZone.zoneId); + } + + const addFloatingWidget = { + organization, + widget: newObject, + zoneId: selectedZone.zoneId, + }; + + if (visualizationSocket) { + visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); + } + + useDroppedObjectsStore + .getState() + .addObject(selectedZone.zoneName, newObject); + + const droppedObjectsStore = useDroppedObjectsStore.getState(); + const currentZone = droppedObjectsStore.zones[selectedZone.zoneName]; + + if (currentZone && currentZone.zoneId === selectedZone.zoneId) { + console.log( + `Objects for Zone ${selectedZone.zoneId}:`, + currentZone.objects + ); + setFloatingWidget(currentZone.objects); + } else { + console.warn("Zone not found or zoneId mismatch"); + } + } catch (error) { + console.error("Error in handleDrop:", error); + } +}; diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index bdefb8b..17a6315 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -13,6 +13,7 @@ import { useWallItems, useZones, useLoadingProgress, + useWidgetSubOption, } from "../store/store"; import { useNavigate } from "react-router-dom"; import { usePlayButtonStore } from "../store/usePlayButtonStore"; @@ -22,12 +23,19 @@ import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys"; import { useSelectedUserStore } from "../store/useCollabStore"; import FollowPerson from "../components/templates/FollowPerson"; -import ProductionCapacity from "../components/ui/analysis/ProductionCapacity"; -import ThroughputSummary from "../components/ui/analysis/ThroughputSummary"; -import ROISummary from "../components/ui/analysis/ROISummary"; +import Scene from "../modules/scene/scene"; +import { createHandleDrop } from "../modules/visualization/functions/handleUiDrop"; +import { useSelectedZoneStore } from "../store/visualization/useZoneStore"; +import { useFloatingWidget } from "../store/visualization/useDroppedObjectsStore"; +import { useLogger } from "../components/ui/log/LoggerContext"; +import RenderOverlay from "../components/templates/Overlay"; +import LogList from "../components/ui/log/LogList"; +import Footer from "../components/footer/Footer"; const Project: React.FC = () => { let navigate = useNavigate(); + const echo = useLogger(); + const { activeModule, setActiveModule } = useModuleStore(); const { loadingProgress } = useLoadingProgress(); const { setUserName } = useUserName(); @@ -50,42 +58,79 @@ const Project: React.FC = () => { setOrganization(Organization); setUserName(name); } + echo.info("Log in success full"); } else { navigate("/"); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const { isPlaying } = usePlayButtonStore(); + // global store const { toggleThreeD } = useThreeDStore(); + + // simulation store + const { isPlaying } = usePlayButtonStore(); + + // collaboration store const { selectedUser } = useSelectedUserStore(); + const { isLogListVisible } = useLogger(); + + // real-time visualization store + const { widgetSubOption } = useWidgetSubOption(); + const { visualizationSocket } = useSocketStore(); + const { selectedZone } = useSelectedZoneStore(); + const { setFloatingWidget } = useFloatingWidget(); return (
- {/*
-
- - -
- -
*/} - - {loadingProgress > 0 && } - {!isPlaying && ( + {!selectedUser && ( <> - {toggleThreeD && } - - + + {loadingProgress > 0 && } + {!isPlaying && ( + <> + {toggleThreeD && } + + + + )} + + {activeModule === "market" && } + {activeModule !== "market" && } + {isPlaying && activeModule === "simulation" && } )} - {/* - - */} - {activeModule === "market" && } - - {activeModule !== "market" && } - {isPlaying && activeModule === "simulation" && } - {/* {} */} +
+ createHandleDrop({ + widgetSubOption, + visualizationSocket, + selectedZone, + setFloatingWidget, + event, + }) + } + onDragOver={(event) => event.preventDefault()} + > + +
{selectedUser && } + {isLogListVisible && ( + + + + )} +
); }; diff --git a/app/src/store/visualization/useDroppedObjectsStore.ts b/app/src/store/visualization/useDroppedObjectsStore.ts index 5c4527b..bbe4cde 100644 --- a/app/src/store/visualization/useDroppedObjectsStore.ts +++ b/app/src/store/visualization/useDroppedObjectsStore.ts @@ -1,5 +1,4 @@ import { create } from "zustand"; -import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets"; import { useSocketStore } from "../store"; import useChartStore from "./useChartStore"; diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index f4a6495..29976dc 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -22,7 +22,11 @@ $text-button-color-dark: #f3f3fd; // background colors // ---------- light mode ---------- $background-color: linear-gradient(-45deg, #fcfdfd71 0%, #fcfdfd79 100%); -$background-color-solid-gradient: linear-gradient(-45deg, #fcfdfd 0%, #fcfdfd 100%); +$background-color-solid-gradient: linear-gradient( + -45deg, + #fcfdfd 0%, + #fcfdfd 100% +); $background-color-solid: #fcfdfd; $background-color-secondary: #fcfdfd4d; $background-color-accent: #6f42c1; @@ -45,7 +49,11 @@ $background-radial-gray-gradient: radial-gradient( // ---------- dark mode ---------- $background-color-dark: linear-gradient(-45deg, #333333b3 0%, #2d2437b3 100%); -$background-color-solid-gradient-dark: linear-gradient(-45deg, #333333 0%, #2d2437 100%); +$background-color-solid-gradient-dark: linear-gradient( + -45deg, + #333333 0%, + #2d2437 100% +); $background-color-solid-dark: #19191d; $background-color-secondary-dark: #19191d99; $background-color-accent-dark: #6f42c1; @@ -104,6 +112,21 @@ $color3: #b186ff; $color4: #8752e8; $color5: #c7a8ff; +// log indication colors +// ------------ text ------------- +$log-default-text-color: #6f42c1; +$log-info-text-color: #488ef6; +$log-warn-text-color: #f3a50c; +$log-error-text-color: #f65648; +$log-success-text-color: #43c06d; + +// ------------ background ------------- +$log-default-backgroung-color: #6e42c133; +$log-info-background-color: #488ef633; +$log-warn-background-color: #f3a50c33; +$log-error-background-color: #f6564833; +$log-success-background-color: #43c06d33; + // old variables $accent-color: #6f42c1; $accent-color-dark: #c4abf1; diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index bf53164..4555ed9 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -37,11 +37,9 @@ // old colors --accent-color: #{$accent-color}; - --highlight-accent-color: #{$highlight-accent-color}; --accent-gradient-color: #{$acent-gradient}; --faint-gradient-color: #{$faint-gradient}; --background-color-gray: #{$background-color-gray}; - --border-color: #{$border-color}; --shadow-main-light: #{$shadow-color}; --box-shadow-light: 0px 2px 4px var(--shadow-main-light); --box-shadow-medium: 0px 4px 8px var(--shadow-main-light); @@ -75,7 +73,7 @@ --background-radial-gray-gradient: #{$background-radial-gray-gradient-dark}; // border colors - --border-color: #{$border-color}; + --border-color: #{$border-color-dark}; --input-border-color: #{$input-border-color-dark}; --border-color-accent: #{$border-color-accent-dark}; @@ -89,11 +87,9 @@ // old colors --accent-color: #{$accent-color-dark}; - --highlight-accent-color: #{$highlight-accent-color-dark}; --accent-gradient-color: #{$acent-gradient-dark}; --faint-gradient-color: #{$faint-gradient-dark}; --background-color-gray: #{$background-color-gray-dark}; - --border-color: #{$border-color-dark}; --shadow-main-dark: #{$shadow-color}; --box-shadow-light: 0px 2px 4px var(--shadow-main-dark); --box-shadow-medium: 0px 4px 8px var(--shadow-main-dark); diff --git a/app/src/styles/base/global.scss b/app/src/styles/base/global.scss index d90e6fb..f13344e 100644 --- a/app/src/styles/base/global.scss +++ b/app/src/styles/base/global.scss @@ -1,11 +1,30 @@ @use "../abstracts/variables" as *; @use "../abstracts/mixins" as *; -section, .section{ - padding: 4px; - outline: 1px solid var(--border-color); - outline-offset: -1px; - border-radius: #{$border-radius-large}; - background: var(--background-color); - margin: 4px 0; +section, +.section { + padding: 4px; + outline: 1px solid var(--border-color); + outline-offset: -1px; + border-radius: #{$border-radius-large}; + background: var(--background-color); + margin: 4px 0; +} + +.scene-container { + width: calc(100% - (320px + 270px + 90px)); + height: calc(100% - (250px)); + position: absolute; + top: 50%; + left: calc(270px + 45px); + overflow: hidden; + z-index: 1; + transform: translate(0, -50%); + transition: all 0.2s; + box-shadow: $box-shadow-medium; + background: var(--background-color-solid); + canvas { + outline: none; + border: none; + } } diff --git a/app/src/styles/base/reset.scss b/app/src/styles/base/reset.scss index ab77f9a..82d286e 100644 --- a/app/src/styles/base/reset.scss +++ b/app/src/styles/base/reset.scss @@ -12,10 +12,3 @@ input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button input[type="password"]::-webkit-inner-spin-button { /* Just in case */ display: none; } - -button{ - border: none; - outline: none; - background: none; - cursor: pointer; -} \ No newline at end of file diff --git a/app/src/styles/components/analysis/ROISummary.scss b/app/src/styles/components/analysis/ROISummary.scss deleted file mode 100644 index 96b4a5d..0000000 --- a/app/src/styles/components/analysis/ROISummary.scss +++ /dev/null @@ -1,311 +0,0 @@ -.roiSummary-container { - .roiSummary-wrapper { - background-color: var(--background-color); - - .product-info { - display: flex; - } - - .playBack { - display: flex; - background-color: var(--background-color); - border-radius: 12px; - padding: 6px; - - .info { - span { - font-size: var(--font-size-xlarge); - - &:first-child { - color: #31C756; - } - - &:last-child { - color: var(--text-color); - } - } - } - } - - .roi-details { - display: flex; - align-items: center; - gap: 12px; - - .progress-wrapper { - width: 250px; - display: flex; - flex-direction: column; - gap: 6px; - - .content { - display: flex; - flex-direction: column; - gap: 3px; - align-items: center; - - .key { - font-size: var(--font-size-xlarge); - color: var(--accent-color); - } - } - } - - .roi-progress { - width: 100%; - } - - .metrics { - display: flex; - flex-direction: column; - gap: 6px; - - .metric-item { - width: 100%; - border-radius: 6px; - border: 1px solid #00FF56; - background: #436D51; - display: flex; - flex-direction: column; - padding: 4px 6px; - - &:last-child { - align-items: center; - } - - .metric-label { - font-size: 10px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .metric-value { - text-align: center; - line-height: 20px; - } - } - - .metric-wrapper { - display: flex; - gap: 6px; - - .metric-item { - background-color: var(--background-color); - border: 1px solid var(--Grays-Gray-6, #F2F2F7); - } - } - } - } - - .cost-breakdown { - background-color: var(--background-color); - border: 1px solid var(--text-disabled); - border-radius: 8px; - padding: 16px; - - .breakdown-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - margin-bottom: 16px; - - .section-wrapper { - display: flex; - gap: 4px; - align-items: center; - } - - .section-number { - font-size: 20px; - color: #00aaff; - } - - .section-title { - font-size: var(--font-size-regular); - color: var(--text-color); - } - - .expand-icon { - font-size: 16px; - color: var(--text-color); - cursor: pointer; - transform: rotate(90deg); - transition: transform 0.2s linear; - } - - .expand-icon.open { - transform: rotate(0deg); - - } - } - - .breakdown-table { - width: 100%; - border-collapse: collapse; - border-radius: 8px; - - th, - td { - padding: 8px; - text-align: left; - border-top: 1px solid var(--text-disabled); - border-bottom: 1px solid var(--text-disabled); - } - - th:first-child, - td:first-child { - border-left: 1px solid var(--text-disabled); - } - - th:last-child, - td:last-child { - border-right: 1px solid var(--text-disabled); - } - - th { - background-color: var(--background-color); - color: #333; - } - - .total-row, - .net-profit-row { - font-weight: bold; - color: #333; - } - } - } - - .tips-section { - background-color: var(--background-color); - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 6px; - padding: 12px; - - .tip-header { - display: flex; - align-items: center; - - .tip-title { - color: var(--text-color); - font-weight: 600; - } - } - - .tip-description { - span { - font-size: var(--font-size-xlarge); - color: #34C759; - - &:first-child { - color: #34C759; - } - - &:nth-child(2) { - color: #488EF6; - } - } - } - } - - .get-tips-button { - border: none; - border-radius: 5px; - cursor: pointer; - font-size: 14px; - margin-top: 8px; - display: inline-block; - display: flex; - justify-content: flex-end; - background: none; - - .btn { - background-color: var(--accent-color); - color: var(--background-color); - padding: 4px 6px; - border-radius: 5px; - display: inline-block; - font-size: 14px; - text-align: center; - } - } - } - - .semi-circle-wrapper { - width: 100%; - height: 125px; - overflow-y: hidden; - position: relative; - .semi-circle { - width: 100%; - height: 250px; - border-radius: 50%; - position: relative; - transition: background 0.5s ease; - } - .progress-cover { - position: absolute; - width: 75%; - height: 75%; - top: 12.5%; - left: 12.5%; - background: #000000cc; - border-radius: 50%; - } - } - - - - .label-wrapper { - .label { - font-size: var(--font-size-xxxlarge); - } - - position: absolute; - bottom: 0%; - left: 50%; - transform: translate(-50%, 0%); - font-weight: bold; - font-size: 1.2rem; - color: #333; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - } -} - -// Breakdown Table Open/Close Logic - -.breakdown-table-wrapper { - &.closed { - max-height: 0; - padding: 0; - } - - &.open { - max-height: 500px; - } - - - - - - .breakdown-table { - width: 100%; - border-collapse: collapse; - - th, - td { - padding: 10px; - border: 1px solid #ddd; - text-align: left; - } - - - - } -} \ No newline at end of file diff --git a/app/src/styles/components/analysis/analysis.scss b/app/src/styles/components/analysis/analysis.scss deleted file mode 100644 index 030a79f..0000000 --- a/app/src/styles/components/analysis/analysis.scss +++ /dev/null @@ -1,279 +0,0 @@ -.analysis { - position: fixed; - top: 0; - left: 0; - display: flex; - justify-content: space-between; - align-items: start; - width: 100%; - height: 100vh; - // pointer-events: none; - z-index: 10000; - - .analysis-wrapper { - display: flex; - flex-direction: column; - gap: 12px; - } -} - -.analysis-card { - min-width: 333px; - background: var(--background-color); - border-radius: 20px; - - padding: 8px; - - .analysis-card-wrapper { - width: 100%; - background: var(--background-color); - border-radius: 14px; - padding: 16px; - - display: flex; - flex-direction: column; - gap: 14px; - - .card-header { - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - - .main-header { - line-height: 20px; - font-size: var(--font-size-regular); - } - } - - .process-container { - display: flex; - flex-direction: column; - - .throughput-value { - font-size: 1rem; - - .value { - font-weight: bold; - font-size: 1.5rem; - } - } - - .progress-bar-wrapper { - display: flex; - gap: 8px; - margin-top: 6px; - } - - .progress-bar { - position: relative; - // width: 36px; - width: 100%; - height: 4px; - border-radius: 13px; - overflow: hidden; - background: #FBEBD7; - - .bar-fill { - position: absolute; - height: 100%; - top: 0; - left: 0; - background: #FC9D2F; - border-radius: 13px; - } - - .bar-fill.full { - width: 100%; - } - - .bar-fill.partial { - width: 0; // inline style will override this - } - } - } - - .metrics-section { - padding-top: 16px; - border-top: 1px solid var(--background-color-gray); - - .metric { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 14px; - margin-bottom: 8px; - - .label { - color: var(--text-color); - } - - .value { - font-weight: bold; - } - } - } - } -} - - -.throughoutSummary { - .throughoutSummary-wrapper { - .process-container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - gap: 16px; - width: 100%; - - .throughput-value { - font-size: var(--font-size-small); - flex: 1; - display: flex; - flex-direction: column; - - .value { - color: var(--accent-color); - } - - /* Let the text take available space */ - } - - .lineChart { - max-width: 200px; - height: 100px; - position: relative; - - .assetUsage { - text-align: right; - position: absolute; - right: 0; - top: 0; - } - - canvas { - background: transparent; - } - } - } - - .footer { - display: flex; - gap: 16px; // Space between cards - margin-top: 24px; - - .footer-card { - width: 100%; - background: var(--background-color-gray); - border-radius: 6px; - padding: 8px; - display: flex; - flex-direction: column; - gap: 6px; - - &:first-child { - width: 85%; - } - - .header { - font-size: var(--font-size-regular); - } - - .value-container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: end; - gap: 6px; - } - } - - .shiftUtilization { - .value-container { - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - - .value { - font-size: var(--font-size-xlarge); - } - - .progress-wrapper { - width: 100%; - display: flex; - gap: 6px; - - .progress { - border-radius: 6px; - height: 5px; - - &:nth-child(1) { - background: #F3C64D; - } - - &:nth-child(2) { - background: #67B3F4; - } - - &:nth-child(3) { - background: #7981F5; - } - } - } - - .progress-indicator { - display: flex; - justify-content: space-between; - width: 100%; - gap: 6px; - - .shift-wrapper { - display: flex; - align-items: center; - gap: 5px; - - /* Align items vertically */ - &:nth-child(1) { - .indicator { - - background: #F3C64D; - } - } - - &:nth-child(2) { - .indicator { - - background: #67B3F4; - } - } - - &:nth-child(3) { - .indicator { - - background: #7981F5; - } - } - - label { - font-size: var(--font-size-small); - position: relative; - } - - .indicator { - display: inline-block; - width: 5px; - height: 5px; - border-radius: 50%; - - } - } - } - } - } - - } - - - } -} \ No newline at end of file diff --git a/app/src/styles/components/button.scss b/app/src/styles/components/button.scss index e69de29..dad9120 100644 --- a/app/src/styles/components/button.scss +++ b/app/src/styles/components/button.scss @@ -0,0 +1,24 @@ +@use "../abstracts/variables" as *; +@use "../abstracts/mixins" as *; + +.labeled-button-container { + @include flex-space-between; + padding: 6px 12px; + + button { + padding: 2px 32px; + border: none; + border-radius: #{$border-radius-large}; + color: var(--text-button-color); + background: var(--background-color-button); + transition: all 0.2s; + cursor: pointer; + } +} + +button { + border: none; + outline: none; + background: none; + cursor: pointer; +} diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/footer.scss new file mode 100644 index 0000000..b2d85d0 --- /dev/null +++ b/app/src/styles/components/footer/footer.scss @@ -0,0 +1,71 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + +.footer-wrapper { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + z-index: 1; + display: flex; + justify-content: space-between; + padding: 2px 12px; + + .selection-wrapper { + display: flex; + gap: 6px; + + .selector-wrapper { + display: flex; + gap: 6px; + align-items: center; + background: var(--background-color); + padding: 3px 6px; + border-radius: 12px; + color: var(--text-color); + + .selector { + color: var(--text-color); + } + } + } + + .logs-wrapper { + display: flex; + gap: 6px; + + .logs-detail, + .version { + border-radius: 12px; + background: var(--background-color); + padding: 3px 6px; + color: var(--text-color); + display: flex; + align-items: center; + gap: 6px; + } + + .logs-detail { + padding: 2px 12px; + cursor: pointer; + .log-icon { + @include flex-center; + } + .log-message { + max-width: 40vw; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .version { + font-size: var(--font-size-tiny); + display: flex; + gap: 6px; + .icon{ + @include flex-center; + } + } + } +} diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index e3b9585..d4c6544 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -639,21 +639,6 @@ input[type="number"] { } } -.labeled-button-container { - @include flex-space-between; - padding: 6px 12px; - - button { - padding: 2px 32px; - border: none; - border-radius: #{$border-radius-large}; - color: var(--text-button-color); - background: var(--background-color-button); - transition: all 0.2s; - cursor: pointer; - } -} - .value-field-container { margin-bottom: 6px; padding: 6px 12px; diff --git a/app/src/styles/components/layouts.scss b/app/src/styles/components/layouts.scss deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/logs.scss new file mode 100644 index 0000000..f07708b --- /dev/null +++ b/app/src/styles/components/logs/logs.scss @@ -0,0 +1,105 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + +.log-list-container { + width: 100vw; + height: 100vh; + background: var(--background-color-secondary); + + .log-list-wrapper { + height: 50%; + min-width: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 5; + background: var(--background-color); + padding: 14px 12px; + border-radius: 15px; + display: flex; + flex-direction: column; + gap: 12px; + backdrop-filter: blur(50px); + outline: 1px solid var(--border-color); + + .log-header { + display: flex; + justify-content: space-between; + + .log-header-wrapper { + display: flex; + align-items: center; + gap: 6px; + } + + .close { + @include flex-center; + height: 28px; + width: 28px; + cursor: pointer; + svg { + scale: 1.6; + } + } + } + + .log-nav-wrapper { + display: flex; + gap: 6px; + + .log-nav { + padding: 8px 16px; + border-radius: 19px; + } + + .log-nav.active { + background-color: var(--background-color-accent); + color: var(--text-button-color); + } + } + + .log-entry-wrapper { + height: 100%; + display: flex; + flex-direction: column; + gap: 4px; + background: var(--background-color); + padding: 18px 10px; + border-radius: 16px; + outline: 1px solid var(--border-color); + outline-offset: -1px; + + .log-entry { + padding: 4px; + border-radius: 4px; + font-size: var(--font-size-small); + display: flex; + align-items: center; + gap: 6px; + + .log-icon { + @include flex-center; + } + .log-entry-message-container { + @include flex-space-between; + gap: 12px; + width: 100%; + .message-time { + font-size: var(--font-size-tiny); + font-weight: 300; + opacity: 0.8; + text-wrap: nowrap; + } + .log-entry-message{ + width: 100%; + } + } + + &:nth-child(odd) { + background: var(--background-color); + } + } + } + } +} diff --git a/app/src/styles/components/simulation/analysis.scss b/app/src/styles/components/simulation/analysis.scss new file mode 100644 index 0000000..9aed5c8 --- /dev/null +++ b/app/src/styles/components/simulation/analysis.scss @@ -0,0 +1,557 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + +.analysis { + position: fixed; + top: 0; + left: 0; + display: flex; + justify-content: space-between; + align-items: start; + 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; + pointer-events: all; + + .analysis-card-wrapper { + width: 100%; + background: var(--background-color); + border-radius: 14px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 14px; + backdrop-filter: blur(10px); + outline: 1px solid var(--border-color); + outline-offset: -1px; + + .card-header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + + .main-header { + line-height: 20px; + font-size: var(--font-size-regular); + } + } + + .process-container { + display: flex; + flex-direction: column; + + .throughput-value { + font-size: 1rem; + + .value { + font-weight: bold; + font-size: 1.5rem; + } + } + + .progress-bar-wrapper { + display: flex; + gap: 8px; + margin-top: 6px; + } + + .progress-bar { + position: relative; + width: 100%; + height: 4px; + border-radius: 13px; + overflow: hidden; + background: #fbebd7; + + .bar-fill { + position: absolute; + height: 100%; + top: 0; + left: 0; + background: #fc9d2f; + border-radius: 13px; + } + + .bar-fill.full { + width: 100%; + } + + .bar-fill.partial { + width: 0; // inline style will override this + } + } + } + + .metrics-section { + padding-top: 16px; + border-top: 1px solid var(--background-color-gray); + + .metric { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + margin-bottom: 8px; + + .label { + color: var(--text-color); + } + + .value { + font-weight: bold; + } + } + } + } + .throughoutSummary-wrapper { + .process-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 16px; + width: 100%; + + .throughput-value { + font-size: var(--font-size-small); + flex: 1; + display: flex; + flex-direction: column; + + .value { + color: var(--accent-color); + } + + /* Let the text take available space */ + } + + .lineChart { + max-width: 200px; + height: 100px; + position: relative; + + .assetUsage { + text-align: right; + position: absolute; + right: 0; + top: 0; + } + + canvas { + background: transparent; + } + } + } + + .footer { + display: flex; + gap: 16px; // Space between cards + margin-top: 24px; + + .footer-card { + width: 100%; + background: var(--background-color-gray); + border-radius: 6px; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + + &:first-child { + width: 85%; + } + + .header { + font-size: var(--font-size-regular); + } + + .value-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: end; + gap: 6px; + } + } + + .shiftUtilization { + .value-container { + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + + .value { + font-size: var(--font-size-xlarge); + } + + .progress-wrapper { + width: 100%; + display: flex; + gap: 6px; + + .progress { + border-radius: 6px; + height: 5px; + + &:nth-child(1) { + background: #f3c64d; + } + + &:nth-child(2) { + background: #67b3f4; + } + + &:nth-child(3) { + background: #7981f5; + } + } + } + + .progress-indicator { + display: flex; + justify-content: space-between; + width: 100%; + gap: 6px; + + .shift-wrapper { + display: flex; + align-items: center; + gap: 5px; + + /* Align items vertically */ + &:nth-child(1) { + .indicator { + background: #f3c64d; + } + } + + &:nth-child(2) { + .indicator { + background: #67b3f4; + } + } + + &:nth-child(3) { + .indicator { + background: #7981f5; + } + } + + label { + font-size: var(--font-size-small); + position: relative; + } + + .indicator { + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + } + } + } + } + } + } + } + } + .roiSummary-wrapper { + max-width: 470px; + background-color: var(--background-color); + + .product-info { + display: flex; + } + + .playBack { + display: flex; + background-color: var(--background-color); + border-radius: 12px; + padding: 6px; + + .info { + span { + font-size: var(--font-size-xlarge); + + &:first-child { + color: #31c756; + } + + &:last-child { + color: var(--text-color); + } + } + } + } + + .roi-details { + display: flex; + align-items: center; + gap: 12px; + + .progress-wrapper { + width: 250px; + display: flex; + flex-direction: column; + gap: 6px; + + .content { + display: flex; + flex-direction: column; + gap: 3px; + align-items: center; + + .key { + font-size: var(--font-size-xlarge); + color: var(--accent-color); + } + } + } + + .roi-progress { + width: 100%; + } + + .metrics { + display: flex; + flex-direction: column; + gap: 6px; + + .metric-item { + width: 100%; + border-radius: #{$border-radius-xxx}; + border: 1px solid #00ff56; + background: #17eb5e42; + display: flex; + flex-direction: column; + padding: 4px 8px; + + &:last-child { + align-items: center; + } + + .metric-label { + opacity: 0.8; + font-size: 10px; + font-weight: 300; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .metric-value { + text-align: center; + line-height: 20px; + } + } + + .metric-wrapper { + display: flex; + gap: 6px; + + .metric-item { + border-radius: #{$border-radius-large}; + background-color: var(--background-color); + border: 1px solid var(--border-color); + } + } + } + } + + .cost-breakdown { + background-color: var(--background-color); + border: 1px solid var(--border-color); + border-radius: #{$border-radius-extra-large}; + padding: 16px; + + .breakdown-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + + .section-wrapper { + display: flex; + gap: 4px; + align-items: center; + } + + .section-number { + color: #00aaff; + } + + .section-title { + font-size: var(--font-size-regular); + color: var(--text-color); + } + + .expand-icon { + font-size: 16px; + color: var(--text-color); + cursor: pointer; + transform: rotate(90deg); + transition: transform 0.2s linear; + } + + .expand-icon.open { + transform: rotate(0deg); + } + } + + .breakdown-table { + width: 100%; + border-collapse: collapse; + border-radius: 8px; + overflow: hidden; + outline: 1px solid var(--border-color); + outline-offset: -1px; + margin-top: 12px; + + th, + td { + color: var(--text-color); + padding: 8px; + text-align: left; + border: 1px solid var(--border-color); + } + th { + background-color: var(--background-color); + } + } + } + + .tips-section { + background-color: var(--background-color); + border-radius: #{$border-radius-large}; + outline: 1px solid var(--border-color); + display: flex; + flex-direction: column; + gap: 6px; + padding: 12px; + + .tip-header { + display: flex; + align-items: center; + + .tip-title { + color: var(--text-color); + font-weight: 600; + } + } + + .tip-description { + span { + font-size: var(--font-size-xlarge); + color: #34c759; + + &:first-child { + color: #34c759; + } + + &:nth-child(2) { + color: #488ef6; + } + } + } + } + + .get-tips-button { + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + margin-top: 8px; + display: inline-block; + display: flex; + justify-content: flex-end; + background: none; + + .btn { + color: var(--text-button-color); + background: var(--background-color-button); + padding: 4px 12px; + border-radius: #{$border-radius-large}; + display: inline-block; + text-align: center; + } + } + } + + .semi-circle-wrapper { + width: 100%; + height: 125px; + overflow-y: hidden; + position: relative; + .semi-circle { + width: 100%; + height: 250px; + border-radius: 50%; + position: relative; + } + .progress-cover { + position: absolute; + width: 75%; + height: 75%; + top: 12.5%; + left: 12.5%; + border-radius: 50%; + } + } + + .label-wrapper { + .label { + font-size: var(--font-size-xxxlarge); + } + + position: absolute; + bottom: 0%; + left: 50%; + transform: translate(-50%, 0%); + font-weight: bold; + font-size: 1.2rem; + color: #333; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + } +} + +.breakdown-table-wrapper { + &.closed { + max-height: 0; + padding: 0; + } + + &.open { + max-height: 500px; + } + + .breakdown-table { + width: 100%; + border-collapse: collapse; + + th, + td { + padding: 10px; + border: 1px solid #ddd; + text-align: left; + } + } +} + +// Breakdown Table Open/Close Logic + diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss index 0fc3df8..88f85dc 100644 --- a/app/src/styles/components/simulation/simulation.scss +++ b/app/src/styles/components/simulation/simulation.scss @@ -32,10 +32,17 @@ } .controls-container { - @include flex-center; + @include flex-space-between; gap: 12px; justify-content: space-between; - + .header{ + @include flex-center; + gap: 6px; + padding: 0 8px; + svg{ + scale: 1.3; + } + } .production-details, .controls-wrapper { @include flex-center; @@ -72,7 +79,7 @@ .expand-icon-container { @include flex-center; - padding: 6px 8px; + padding: 0 8px; cursor: pointer; } @@ -307,6 +314,22 @@ font-size: var(--font-size-tiny); } + .timmer { + width: auto; + position: absolute; + bottom: 0; + font-size: var(--font-size-tiny); + } + + .start-displayer { + left: 8px; + } + + .end-displayer { + width: auto; + right: 8px; + } + .start-displayer { bottom: 4px; left: 16px; @@ -347,6 +370,12 @@ } .simulation-player-container.open { + + .start-displayer, + .end-displayer { + display: none; + } + .timmer { display: none; } diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/tools.scss index e476c3c..29d37b4 100644 --- a/app/src/styles/components/tools.scss +++ b/app/src/styles/components/tools.scss @@ -15,7 +15,7 @@ transition: width 0.2s; background: var(--background-color); backdrop-filter: blur(8px); - z-index: #{$z-index-default}; + z-index: 2; outline: 1px solid var(--border-color); outline-offset: -1px; @@ -124,6 +124,8 @@ padding: 4px; border-radius: #{$border-radius-medium}; background: var(--background-color); + outline: 1px solid var(--border-color); + outline-offset: -1px; gap: 5px; position: relative; diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index c7f1694..f8c022f 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -58,12 +58,8 @@ fill: var(--icon-default-color-active); } &:hover { - rect { - stroke: var(--icon-default-color); - } - circle { - fill: var(--icon-default-color); - } + filter: saturate(0.8); + background: var(--background-color-accent); } } } @@ -420,7 +416,7 @@ outline: none; path { stroke: var(--text-button-color); - stroke-width: 1.3; + strokeWidth: 1.3; } } } @@ -686,7 +682,7 @@ path { stroke: var(--accent-color); - stroke-width: 1.5px; + strokeWidth: 1.5px; } &:hover { @@ -714,10 +710,10 @@ .add-button { @include flex-center; - padding: 2px 4px; + padding: 4px 8px; background: var(--background-color-button); color: var(--text-button-color); - border-radius: #{$border-radius-small}; + border-radius: #{$border-radius-large}; cursor: pointer; outline: none; border: none; @@ -832,10 +828,10 @@ transform: translateX(4px); &:hover { - background: var(--accent-color); + background: var(--background-color-accent); path { - stroke: var(--primary-color); + stroke: var(--text-button-color); } } } @@ -1003,10 +999,10 @@ border-radius: 8px 0 0 8px; &:hover { - background: var(--accent-color); + background: var(--background-color-accent); path { - stroke: var(--primary-color); + stroke: var(--text-button-color); } } } @@ -1067,7 +1063,13 @@ .dropdown-content-container { padding: 6px 12px; } - + .value-field-container { + padding: 6px; + .dropdown { + min-width: 44px; + text-align: center; + } + } .input-range-container { .input-container { width: 75%; diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 66a60e7..3adfc0f 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -13,7 +13,6 @@ @use 'components/button'; @use 'components/form'; @use 'components/input'; -@use 'components/layouts'; @use 'components/lists'; @use 'components/moduleToggle'; @use 'components/templates'; @@ -22,11 +21,12 @@ @use 'components/visualization/ui/styledWidgets'; @use 'components/visualization/floating/common'; @use 'components/marketPlace/marketPlace'; -@use 'components/simulation/simulation'; @use 'components/menu/menu'; @use 'components/confirmationPopUp'; -@use 'components/analysis/analysis'; -@use 'components/analysis/ROISummary.scss'; +@use 'components/simulation/simulation'; +@use 'components/simulation/analysis'; +@use 'components/logs/logs'; +@use 'components/footer/footer.scss'; // layout @use 'layout/loading'; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index fa1b86c..abc626a 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -3,8 +3,6 @@ // Main Container .realTime-viz { - background: #131313; - box-shadow: $box-shadow-medium; width: calc(100% - (320px + 270px + 90px)); height: calc(100% - (250px)); position: absolute; @@ -12,8 +10,8 @@ left: calc(270px + 45px); transform: translate(0, -50%); border-radius: #{$border-radius-medium}; - transition: all 0.2s; - z-index: #{$z-index-default}; + z-index: 2; + pointer-events: none; .realTime-viz-wrapper { width: 100%; @@ -39,10 +37,6 @@ z-index: 1; } - .scene-container { - overflow: hidden; - } - .icon { display: flex; align-items: center; @@ -74,6 +68,8 @@ z-index: 3; transform: translate(-50%, -10%); transition: transform 0.5s linear; + pointer-events: all; + &::-webkit-scrollbar { display: none; } @@ -367,6 +363,7 @@ border-radius: 2px; transition: transform 0.3s ease; box-shadow: #{$box-shadow-medium}; + pointer-events: all; svg { stroke: var(--icon-default-color) !important; @@ -426,10 +423,8 @@ path { stroke: #f65648; - stroke-width: 1.3; + strokeWidth: 1.3; } - - } } @@ -778,17 +773,10 @@ } } - - - .panel-content { background: var(--background-color); - } - - - /* RIGHT */ .panel-content.right-opening { animation: rightExpand 0.5s ease-in-out forwards; @@ -913,9 +901,6 @@ } } - - - // Add button .extra-Bs-addopening { @@ -926,7 +911,6 @@ animation: slideUp 0.3s ease forwards; } - @keyframes slideDown { from { opacity: 0;