From 640505a7f70034a71d43e8b1856b56d7020f4e31 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Fri, 23 May 2025 12:09:07 +0530 Subject: [PATCH] feat: enhance Design component with improved styling and functionality for element customization feat: update LogList and LoggerContext to manage log visibility and selected tab state refactor: clean up DraggableWidget by removing unnecessary console logs refactor: remove debug logs from TotalCardComponent style: improve sidebar styles for better layout and appearance --- .../visualization/design/Design.tsx | 412 +++++------------- app/src/components/ui/log/LogList.tsx | 17 +- app/src/components/ui/log/LoggerContext.tsx | 57 ++- .../widgets/2d/DraggableWidget.tsx | 4 - .../floating/cards/TotalCardComponent.tsx | 3 +- app/src/styles/layout/sidebar.scss | 111 ++++- 6 files changed, 270 insertions(+), 334 deletions(-) diff --git a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx index f45cf35..a70c887 100644 --- a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx +++ b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx @@ -1,324 +1,146 @@ -import { useState, useEffect, useRef } from "react"; +import React, { useEffect, useState } from "react"; +import { ArrowIcon } from "../../../../icons/ExportCommonIcons"; +import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; +import InputRange from "../../../../ui/inputs/InputRange"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent"; -import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import { WalletIcon } from "../../../../icons/3dChartIcons"; -import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; -interface Widget { - id: string; - type?: string; - panel: "top" | "bottom" | "left" | "right"; - title?: string; - header?: string; - fontFamily?: string; - fontSize?: string; - fontWeight?: string; - className?: string; - data?: { - labels: string[]; - datasets: { - data: number[]; - backgroundColor: string; - borderColor: string; - borderWidth: number; - }[]; - }; - value?: string; - per?: string; -} +const defaultStyle = { + theme: "Glass", + elementColor: "#ffffff", + blurEffect: 10, + opacity: 10, + selectedElement: "Glass", +}; -interface ChartElement { - tagName: string; - className: string; - textContent: string; - selector: string; -} +const defaultChartData = { + duration: "1h", + measurements: {}, + datasets: [ + { + data: [65, 59, 80, 81, 56, 55, 40], + backgroundColor: "#6f42c1", + borderColor: "#b392f0", + borderWidth: 1, + }, + ], + labels: ["January", "February", "March", "April", "May", "June", "July"], +}; const Design = () => { - const [selectedFont, setSelectedFont] = useState("drop down"); - const [selectedSize, setSelectedSize] = useState("drop down"); - const [selectedWeight, setSelectedWeight] = useState("drop down"); - const [elementColor, setElementColor] = useState("#6f42c1"); - const [showColorPicker, setShowColorPicker] = useState(false); - const [chartElements, setChartElements] = useState([]); - const [selectedElementToStyle, setSelectedElementToStyle] = useState< - string | null - >(null); - const [nameInput, setNameInput] = useState(""); - const chartRef = useRef(null); + const { selectedChartId } = useWidgetStore(); + const [styles, setStyles] = useState>({}); - const { selectedChartId, setSelectedChartId, widgets, setWidgets } = - useWidgetStore(); + const currentStyle = selectedChartId + ? styles[selectedChartId.id] || defaultStyle + : defaultStyle; - // Initialize name input and extract elements when selectedChartId changes - useEffect(() => { - setNameInput(selectedChartId?.header ? selectedChartId?.title : ""); - - if (!chartRef.current) return; - - const timer = setTimeout(() => { - const chartContainer = chartRef.current; - if (!chartContainer) return; - - const elements = Array.from(chartContainer.querySelectorAll("*")) - .filter((el) => { - const tagName = el.tagName.toLowerCase(); - return !["script", "style", "meta", "link", "head"].includes(tagName); - }) - .map((el, index) => { - const tagName = el.tagName.toLowerCase(); - const className = - typeof el.className === "string" ? el.className : ""; - const textContent = el.textContent?.trim() ?? ""; - - let selector = tagName; - - if (className && typeof className === "string") { - const classList = className - .split(/\s+/) - .filter((c) => c.length > 0); - if (classList.length > 0) { - selector += "." + classList.join("."); - } - } - - if (!className || className.trim() === "") { - const parent = el.parentElement; - if (parent) { - const siblings = Array.from(parent.children).filter( - (child) => child.tagName.toLowerCase() === tagName - ); - const position = siblings.indexOf(el) + 1; - selector += `:nth-of-type(${position})`; - } - } - - return { - tagName, - className, - textContent, - selector, - }; - }); - - setChartElements(elements); - }, 300); - - return () => clearTimeout(timer); - }, [selectedChartId]); - - const applyStyles = () => { - if (!selectedElementToStyle || !chartRef.current) return; - - const element = chartRef.current.querySelector(selectedElementToStyle); - if (!element) return; - - const elementToStyle = element as HTMLElement; - - if (selectedFont !== "drop down") { - elementToStyle.style.fontFamily = selectedFont; - } - if (selectedSize !== "drop down") { - elementToStyle.style.fontSize = selectedSize; - } - if (selectedWeight !== "drop down") { - elementToStyle.style.fontWeight = selectedWeight.toLowerCase(); - } - if (elementColor) { - elementToStyle.style.color = elementColor; - } - }; - - useEffect(() => { - applyStyles(); - }, [ - selectedFont, - selectedSize, - selectedWeight, - elementColor, - selectedElementToStyle, - ]); - - const handleUpdateWidget = (updatedProperties: Partial) => { + const updateStyle = (updates: Partial) => { if (!selectedChartId) return; - - const updatedChartId = { - ...selectedChartId, - ...updatedProperties, - }; - setSelectedChartId(updatedChartId); - - const updatedWidgets = widgets.map((widget) => - widget.id === selectedChartId.id - ? { ...widget, ...updatedProperties } - : widget - ); - setWidgets(updatedWidgets); + setStyles((prev) => ({ + ...prev, + [selectedChartId.id]: { ...currentStyle, ...updates }, + })); }; - const handleNameChange = (e: React.ChangeEvent) => { - const newName = e.target.value; - setNameInput(newName); - - if (selectedChartId?.title) { - handleUpdateWidget({ title: newName }); - } else if (selectedChartId?.header) { - handleUpdateWidget({ header: newName }); - } - }; - - const defaultChartData = { - labels: ["January", "February", "March", "April", "May", "June", "July"], - datasets: [ - { - data: [65, 59, 80, 81, 56, 55, 40], - backgroundColor: "#6f42c1", - borderColor: "#b392f0", - borderWidth: 1, - }, - ], - }; - - const elementOptions = chartElements.map((el) => { - let displayName = el.tagName; - if (el.className) displayName += `.${el.className}`; - if (el.textContent) - displayName += ` (${el.textContent.substring(0, 20)}${ - el.textContent.length > 20 ? "..." : "" - })`; - return { - display: displayName, - value: el.selector, - }; - }); + useEffect(() => { + console.log("Styles", styles); + }, [styles]); return (
-
- {selectedChartId?.title ? selectedChartId?.header : "Widget 1"} +
+
+
Appearance
+
+ +
+
+ +
+
+
Theme
+
+ updateStyle({ theme })} + /> +
+
+ + {currentStyle.theme === "Glass" && ( +
+ updateStyle({ blurEffect })} + onPointerUp={() => {}} + /> +
+ )} + + {currentStyle.theme !== "Fill" && ( +
+ updateStyle({ opacity })} + onPointerUp={() => {}} + /> +
+ )} + +
+
Color
+
+ updateStyle({ elementColor: e.target.value })} + /> + + {currentStyle.elementColor} + +
+
+
-
- {selectedChartId?.title ? ( +
+
- ) : ( - - )} -
- -
-
- Element to Style - 0 - ? elementOptions.map((opt) => opt.display) - : ["No elements found"] - } - onSelect={(value) => { - const selected = elementOptions.find( - (opt) => opt.display === value - ); - setSelectedElementToStyle(selected?.value ?? null); + type={selectedChartId?.type ?? "bar"} + title={selectedChartId?.title ?? "Chart"} + data={{ + labels: selectedChartId?.data?.labels ?? defaultChartData.labels, + datasets: selectedChartId?.data?.datasets?.length + ? selectedChartId.data.datasets + : defaultChartData.datasets, }} />
-
- Name - +
+
Name
+
- {selectedChartId?.title && ( -
- Chart Type +
+
Element
+
{ - handleUpdateWidget({ type: value }); - }} + header={currentStyle.selectedElement} + options={["Glass", "Fill", "Transparent"]} + onSelect={(selectedElement) => updateStyle({ selectedElement })} />
- )} - -
- Font Family - setSelectedFont(value)} - /> -
- -
- Size - setSelectedSize(value)} - /> -
- -
- Weight - setSelectedWeight(value)} - /> -
- -
- - - {showColorPicker && ( -
- { - setElementColor(e.target.value); - if (selectedChartId?.data) { - handleUpdateWidget({ - data: { - ...selectedChartId.data, - datasets: [ - { - ...selectedChartId.data.datasets[0], - backgroundColor: e.target.value, - }, - ], - }, - }); - } - }} - /> - {elementColor} -
- )}
diff --git a/app/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx index 10b1cc3..fc905dc 100644 --- a/app/src/components/ui/log/LogList.tsx +++ b/app/src/components/ui/log/LogList.tsx @@ -5,10 +5,14 @@ import { useLogger } from "./LoggerContext"; import { GetLogIcon } from "../../footer/getLogIcons"; const LogList: React.FC = () => { - const { logs, clear, setIsLogListVisible } = useLogger(); - const [selectedTab, setSelectedTab] = useState< - "all" | "info" | "warning" | "error" | "log" | "success" - >("all"); + const { + logs, + clear, + setIsLogListVisible, + isLogListVisible, + selectedTab, + setSelectedTab, + } = useLogger(); const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString(); @@ -18,18 +22,17 @@ const LogList: React.FC = () => { : [...logs].filter((log) => log.type === selectedTab).reverse(); useEffect(() => { - if (logs.length > 0) { + if (isLogListVisible && logs.length > 0) { const lastLog = logs[logs.length - 1]; const validTypes = ["all", "info", "warning", "error"]; if (validTypes.includes(lastLog.type)) { - console.log("lastLog.type: ", lastLog.type); setSelectedTab(lastLog.type); } else { setSelectedTab("all"); } } - }, [logs]); + }, [isLogListVisible]); return ( // eslint-disable-next-line diff --git a/app/src/components/ui/log/LoggerContext.tsx b/app/src/components/ui/log/LoggerContext.tsx index 69a84c1..37eaf6c 100644 --- a/app/src/components/ui/log/LoggerContext.tsx +++ b/app/src/components/ui/log/LoggerContext.tsx @@ -1,5 +1,12 @@ // LoggerProvider.tsx -import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react"; +import React, { + createContext, + useContext, + useState, + useCallback, + useMemo, + useEffect, +} from "react"; import { MathUtils } from "three"; export type LogType = "log" | "info" | "warning" | "error" | "success"; @@ -16,6 +23,8 @@ interface LoggerContextValue { setLogs: React.Dispatch>; isLogListVisible: boolean; setIsLogListVisible: React.Dispatch>; + selectedTab: LogType | "all"; + setSelectedTab: React.Dispatch>; log: (message: string) => void; info: (message: string) => void; warn: (message: string) => void; @@ -31,19 +40,17 @@ export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({ }) => { const [logs, setLogs] = useState([]); const [isLogListVisible, setIsLogListVisible] = useState(false); + const [selectedTab, setSelectedTab] = useState("all"); - const addLog = useCallback( - (type: LogType, message: string) => { - const newLog: LogEntry = { - id: MathUtils.generateUUID(), - type, - message, - timestamp: new Date(), - }; - setLogs((prevLogs) => [...prevLogs, newLog]); - }, - [] - ); + const addLog = useCallback((type: LogType, message: string) => { + const newLog: LogEntry = { + id: MathUtils.generateUUID(), + type, + message, + timestamp: new Date(), + }; + setLogs((prevLogs) => [...prevLogs, newLog]); + }, []); const loggerMethods: LoggerContextValue = useMemo( () => ({ @@ -51,17 +58,33 @@ export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({ setLogs, isLogListVisible, setIsLogListVisible, + selectedTab, + setSelectedTab, log: (message: string) => addLog("log", message), info: (message: string) => addLog("info", message), warn: (message: string) => addLog("warning", message), error: (message: string) => addLog("error", message), success: (message: string) => addLog("success", message), - clear: () => setLogs([]), + clear: () => { + if (selectedTab !== "all") { + setLogs((prevLogs) => + prevLogs.filter((log) => log.type !== selectedTab) + ); + } else { + setLogs([]); + } + }, }), - [logs, setLogs, isLogListVisible, setIsLogListVisible, addLog] + [ + logs, + setLogs, + isLogListVisible, + setIsLogListVisible, + selectedTab, + setSelectedTab, + addLog, + ] ); - - // Attach logger globally to window object useEffect(() => { (window as any).echo = { log: loggerMethods.log, diff --git a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx index aa1c635..fb67a7b 100644 --- a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx +++ b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx @@ -98,8 +98,6 @@ export const DraggableWidget = ({ const deleteSelectedChart = async () => { try { - console.log("delete"); - const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; let deleteWidget = { @@ -111,7 +109,6 @@ export const DraggableWidget = ({ if (visualizationSocket) { setSelectedChartId(null); visualizationSocket.emit("v2:viz-widget:delete", deleteWidget); - console.log("delete widget", selectedChartId); } const updatedWidgets = selectedZone.widgets.filter( (w: Widget) => w.id !== widget.id @@ -313,7 +310,6 @@ export const DraggableWidget = ({ ref={chartWidget} onClick={() => { setSelectedChartId(widget); - console.log("click"); }} > {/* Kebab Icon */} diff --git a/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx b/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx index edc19e6..f3839c6 100644 --- a/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx +++ b/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx @@ -12,12 +12,11 @@ import { } from "../../../../../components/icons/3dChartIcons"; const TotalCardComponent = ({ object }: any) => { - console.log('object: ', object); const [progress, setProgress] = useState(0); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h"); const [name, setName] = useState(object.header ? object.header : ""); - console.log('name: ', name); + const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const { header, flotingDuration, flotingMeasurements } = useChartStore(); diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 9006873..9d114e0 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -829,22 +829,115 @@ display: flex; flex-direction: column; gap: 15px; - padding: 0; font-size: var(--font-weight-regular); color: var(--text-color); + padding: 12px; - .reviewChart { - width: 100%; + .appearance-container, + .element-container { - .floating { - width: 100%; + background: var(--background-color); + backdrop-filter: blur(20px); + border-radius: 15px; + outline: 1px solid var(--border-color); + + padding: 10px; + + display: flex; + flex-direction: column; + gap: 12px; + + .header-container { + padding: 0; + height: auto; + } + + .appearance-style { + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: column; + gap: 12px; + + .regularDropdown-container { + .dropdown-options { + width: 130%; + left: -15%; + } + + .dropdown-header { + gap: 12px; + } + } + + .color-wrapper, + .opacity-wrapper, + .blurEffect-wrapper, + .theme-wrapper { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: column; + + .input-range-container { + width: 100%; + padding: 0; + + .input-container {} + } + } + + .theme-wrapper { + + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + } + + .color-wrapper { + flex-direction: row; + + .value { + display: flex; + align-items: center; + + input { + width: 34px; + height: 24px; + border-radius: 12px; + padding: 0; + } + } + } } } - .selectedWidget { - padding: 6px 12px; - border-top: 1px solid var(--border-color); - border-bottom: 1px solid var(--border-color); + + + .element-container { + padding: 8px; + + .display-element { + width: 100%; + height: 150px; + background: var(--background-color); + backdrop-filter: blur(20px); + border-radius: 5px; + outline: 1px solid var(--border-color); + } + + .name-wrapper, + .element-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + + .value { + width: 60%; + } + } } }