diff --git a/client/package-lock.json b/client/package-lock.json index 4aec4b2..df504a9 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -21,7 +21,8 @@ "react-icons": "^5.5.0", "react-leaflet": "^5.0.0", "react-resizable": "^3.0.5", - "tailwindcss": "^4.1.11" + "tailwindcss": "^4.1.11", + "uuid": "^11.1.0" }, "devDependencies": { "@eslint/js": "^9.29.0", @@ -4776,6 +4777,19 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", diff --git a/client/package.json b/client/package.json index c14d34c..01c5bc5 100644 --- a/client/package.json +++ b/client/package.json @@ -23,7 +23,8 @@ "react-icons": "^5.5.0", "react-leaflet": "^5.0.0", "react-resizable": "^3.0.5", - "tailwindcss": "^4.1.11" + "tailwindcss": "^4.1.11", + "uuid": "^11.1.0" }, "devDependencies": { "@eslint/js": "^9.29.0", diff --git a/client/src/components/layout/Canvas/components/AddWidgetMenu.jsx b/client/src/components/layout/Canvas/components/AddWidgetMenu.jsx new file mode 100644 index 0000000..5ce0553 --- /dev/null +++ b/client/src/components/layout/Canvas/components/AddWidgetMenu.jsx @@ -0,0 +1,35 @@ +import { IoIosArrowBack } from "react-icons/io"; +import { Menu, Portal } from "@chakra-ui/react"; +import { v4 as uuid } from 'uuid'; +import AlertLevel from "./AlertLevelWidget"; + +export default function AddWidgetMenu({onAddWidget, widgetArray, setWidgetArray}) { + function appendWidget(name) { + const newID = uuid(); // could be replaced with something else like guid + setWidgetArray([ // updating widget array containing info on the types of widgets + ...widgetArray, + {i: newID, widget: name} + ]); + onAddWidget(newID); // updating array containing info on layout of widgets + } + + return ( + + + Add Widget + + + + + appendWidget(AlertLevel)} + > + Alert Level + + + + + + ) +} \ No newline at end of file diff --git a/client/src/components/layout/Canvas/components/AlertLevelWidget.jsx b/client/src/components/layout/Canvas/components/AlertLevelWidget.jsx new file mode 100644 index 0000000..ba880ca --- /dev/null +++ b/client/src/components/layout/Canvas/components/AlertLevelWidget.jsx @@ -0,0 +1,10 @@ +import Widget from "./Widget"; +import { Text } from "@chakra-ui/react"; + +export default function AlertLevel({id, isEditable, isDelete, DeleteWidget}) { + return ( + + Input volcano and country + + ); +} \ No newline at end of file diff --git a/client/src/components/layout/Canvas/components/SettingsButton.jsx b/client/src/components/layout/Canvas/components/SettingsButton.jsx index 8937380..4eda93b 100644 --- a/client/src/components/layout/Canvas/components/SettingsButton.jsx +++ b/client/src/components/layout/Canvas/components/SettingsButton.jsx @@ -1,36 +1,33 @@ -// This component displays a drop down menu when clicked, with custom functionality -// for 'Add Widget', 'Edit Widgets', and 'Remove Widgets'. This component will -// control settings related to the Widgets on the main canvas. - -import { Button, IconButton, Menu, Portal } from "@chakra-ui/react"; +import { IconButton, Menu, Portal } from "@chakra-ui/react"; import { FiSettings } from "react-icons/fi"; +import AddWidgetMenu from "./AddWidgetMenu"; -export function SettingsButton({onAddWidget, onEditWidgets, onDeleteWidgets}) { - return ( - // This uses Chakra's built in menu component - - - - - - - - - - Add Widget - Edit Widgets - - Delete Widgets - - - - - - ); +export function SettingsButton({onAddWidget, onEditWidgets, onDeleteWidgets, widgetArray, setWidgetArray}) { + return ( + // This uses Chakra's built in menu component + + + + + + + + + + + Edit Widgets + + Delete Widgets + + + + + + ); } diff --git a/client/src/components/layout/Canvas/components/Widget.jsx b/client/src/components/layout/Canvas/components/Widget.jsx index 74b1b71..d1ecaa2 100644 --- a/client/src/components/layout/Canvas/components/Widget.jsx +++ b/client/src/components/layout/Canvas/components/Widget.jsx @@ -1,13 +1,11 @@ import { useSlotRecipe, chakra, Flex, CloseButton, Text } from "@chakra-ui/react" -export default function Widget({ id, title, isEditable, isDelete, DeleteWidget }) { - // console.log("rendering widget"); +export default function Widget({ id, title, isEditable, isDelete, DeleteWidget, children }) { const recipe = useSlotRecipe({ key: "widget" }); const styles = recipe(); return ( <> - {/* {console.log("This widgets id is: ", id)} */} {isDelete ? ( <> @@ -23,7 +21,7 @@ export default function Widget({ id, title, isEditable, isDelete, DeleteWidget } )} - This is their widget content - drag or resize me! + {children} ); diff --git a/client/src/components/layout/Canvas/views/DashboardCanvas.jsx b/client/src/components/layout/Canvas/views/DashboardCanvas.jsx index 476b6da..38e7ea0 100644 --- a/client/src/components/layout/Canvas/views/DashboardCanvas.jsx +++ b/client/src/components/layout/Canvas/views/DashboardCanvas.jsx @@ -14,22 +14,21 @@ import DeleteAllButton from "../components/DeleteAllButton"; export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChange }) { const [isEditable, setIsEditable] = useState(false); // editable=true means static=false (vice versa) const [isDelete, setIsDelete] = useState(false); - const [newCounter, setNewCounter] = useState(3); const [originalLayout, setOriginalLayout] = useState([]); + const [widgetArray, setWidgetArray] = useState([]); - const onAddWidget = () => { - const newId = newCounter.toString(); + const onAddWidget = (ID) => { setLayout(prev => [ - ...prev, - { - i: newId, - x: (prev.length * 3) % 12, - y: Infinity, - w: 3, - h: 3, - } + ...prev, + { + i: ID, + x: (prev.length * 3) % 12, + y: Infinity, + w: 3, + h: 3, + static: false + } ]); - setNewCounter(prev => prev + 1); }; useEffect(() => { @@ -78,6 +77,7 @@ export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChan const DeleteWidget = (id) => { console.log("Deleting widget with id: ", id); setLayout(layout => layout.filter(w => w.i !== id)); + // add removal for widgetArray here } const DeleteAll = () => { @@ -86,6 +86,7 @@ export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChan ); if (confirm) { setLayout([]); + setWidgetArray([]); onSave(); } else return; @@ -157,6 +158,8 @@ export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChan onAddWidget={ onAddWidget } onEditWidgets={ onEditWidgets } onDeleteWidgets={ onDeleteWidgets } + setWidgetArray={ setWidgetArray } + widgetArray={ widgetArray } /> )} @@ -166,11 +169,13 @@ export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChan {/* CANVAS */} diff --git a/client/src/components/layout/Canvas/views/DashboardCanvasNav/WidgetCanvas.jsx b/client/src/components/layout/Canvas/views/DashboardCanvasNav/WidgetCanvas.jsx index 589979d..0c1779f 100644 --- a/client/src/components/layout/Canvas/views/DashboardCanvasNav/WidgetCanvas.jsx +++ b/client/src/components/layout/Canvas/views/DashboardCanvasNav/WidgetCanvas.jsx @@ -1,50 +1,50 @@ -// This component defines a generic Widget that can be dynamically resized and -// dragged inside the canvas -// Content not included, must be populated using custom charts plots and notifications - -import { useState } from 'react'; import GridLayout, { WidthProvider } from 'react-grid-layout'; -import Widget from '../../components/Widget.jsx'; import { useSlotRecipe, chakra, Flex } from '@chakra-ui/react'; -export default function WidgetCanvas({layout, onLayoutChange, isEditable, isDelete, DeleteWidget}) { +export default function WidgetCanvas({layout, onLayoutChange, isEditable, isDelete, DeleteWidget, widgetArray}) { console.log("rendering widgetcanvas"); - // Define widgets initial position and size - // TODO Add dynamic adding/deleting of widgets via options button - const recipe = useSlotRecipe({ key: "widget" }); - const styles = recipe(); // using root recipe for widget below - const ResponsiveGridLayout = WidthProvider(GridLayout); // canvas width now adjusts to window size + const recipe = useSlotRecipe({ key: "widget" }); + const styles = recipe(); // using root recipe for widget below + const ResponsiveGridLayout = WidthProvider(GridLayout); // canvas width now adjusts to window size + const handleDragStart = () => { document.body.style.userSelect = "none"; }; // don't allow text highlighting when dragging + const handleDragStop = () => { document.body.style.userSelect = ""; }; // allow text highlighting all other times + let merged = []; - // don't allow text highlighting when dragging - const handleDragStart = () => { - document.body.style.userSelect = "none"; - }; - // allow text highlighting all other times - const handleDragStop = () => { - document.body.style.userSelect = ""; - }; + console.log("widgetArray: ", widgetArray); + console.log("layout: ", layout); + if (widgetArray.length && layout.length) { + const widgetMap = new Map(widgetArray.map(item => [item.i, item])); + merged = layout.map(item => { + return { + ...item, + ...widgetMap.get(item.i) + }; + }); + }; - return ( - - - {layout.map((item) => ( - // this flex wrapper allows the widget to shrink down to the header size, no less - - - - ))} - + return ( + + + {merged.map((item) => { + const Widget = item.widget; + return ( + + + + ) + })} + ); } diff --git a/client/src/components/layout/Dashboard.jsx b/client/src/components/layout/Dashboard.jsx index ce1ff90..00a38b6 100644 --- a/client/src/components/layout/Dashboard.jsx +++ b/client/src/components/layout/Dashboard.jsx @@ -1,8 +1,8 @@ -import { Global, css } from "@emotion/react"; import Header from './Header/Header.jsx'; import Sidebar from './Sidebar/Sidebar.jsx'; import Canvas from './Canvas/Canvas.jsx'; import { Grid, GridItem, useRecipe } from '@chakra-ui/react'; +import AlertLevel from './Canvas/components/AlertLevelWidget.jsx'; import { useState } from 'react'; export default function Dashboard() { @@ -12,11 +12,7 @@ export default function Dashboard() { const [darkMode, setDarkMode] = useState(false); // Lifted State - defaults to widget dashboard const [view, setView] = useState("widget"); - const [layout, setLayout] = useState([ - { i: '0', x: 0, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true }, - { i: '1', x: 3, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true }, - { i: '2', x: 6, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true } - ]); + const [layout, setLayout] = useState([]); return ( <> @@ -49,7 +45,7 @@ export default function Dashboard() { setLayout={ setLayout } onLayoutChange={( newLayout ) => setLayout( newLayout )} /> - + );