2025-07-23 11:01:25 -07:00
|
|
|
import Header from './Header/Header.jsx';
|
|
|
|
|
import Sidebar from './Sidebar/Sidebar.jsx';
|
|
|
|
|
import Canvas from './Canvas/Canvas.jsx';
|
2025-08-11 15:10:58 -07:00
|
|
|
import { Grid, GridItem, useRecipe, useDialog } from '@chakra-ui/react';
|
2025-07-23 11:35:28 -07:00
|
|
|
import { useState } from 'react';
|
2025-08-11 15:10:58 -07:00
|
|
|
import DialogPopup from './Canvas/CanvasComponents/DialogPopup.jsx';
|
2025-07-23 11:01:25 -07:00
|
|
|
|
|
|
|
|
export default function Dashboard() {
|
2025-07-28 13:32:03 -07:00
|
|
|
const recipe = useRecipe({ key: "dashboard" });
|
2025-07-23 11:01:25 -07:00
|
|
|
const styles = recipe();
|
2025-08-11 15:10:58 -07:00
|
|
|
const dialog = useDialog();
|
2026-01-30 19:02:31 +00:00
|
|
|
const [darkMode, setDarkMode] = useState(false);
|
2025-07-25 13:19:09 -07:00
|
|
|
|
2026-01-30 19:02:31 +00:00
|
|
|
const [view, setView] = useState("mydashboard");
|
|
|
|
|
|
|
|
|
|
const emptyDash = () => ({
|
|
|
|
|
layout: [],
|
|
|
|
|
widgetArray: [],
|
|
|
|
|
originalLayout: [],
|
|
|
|
|
originalWidgets: [],
|
|
|
|
|
isEditable: false,
|
|
|
|
|
isDelete: false,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Add views as needed to initialize widget canvas views
|
|
|
|
|
const CANVAS_VIEWS = new Set([ "mydashboard", "gas" ]);
|
|
|
|
|
|
|
|
|
|
// Widget dashboard state
|
|
|
|
|
const [dashboards, setDashboards] = useState(() => ({
|
|
|
|
|
mydashboard: emptyDash(),
|
|
|
|
|
gas: emptyDash(),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const isCanvasView = CANVAS_VIEWS.has(view);
|
|
|
|
|
const dash = dashboards[view] ?? emptyDash();
|
|
|
|
|
|
|
|
|
|
const updateDash = (key, updater) => {
|
|
|
|
|
setDashboards(prev => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[key]: updater(prev[key] ?? emptyDash()),
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Widget setter functions
|
|
|
|
|
const setLayout = (fnOrValue) => {
|
|
|
|
|
if (!isCanvasView) return;
|
|
|
|
|
updateDash(view, s => ({
|
|
|
|
|
...s,
|
|
|
|
|
layout: typeof fnOrValue === "function" ? fnOrValue( s.layout ) : fnOrValue,
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setWidgetArray = (fnOrValue) => {
|
|
|
|
|
if (!isCanvasView) return;
|
|
|
|
|
updateDash(view, s => ({
|
|
|
|
|
...s,
|
|
|
|
|
widgetArray: typeof fnOrValue === "function" ? fnOrValue(s.widgetArray) : fnOrValue,
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setIsEditable = (value) => {
|
|
|
|
|
if (!isCanvasView) return;
|
|
|
|
|
updateDash(view, s => ({ ...s, isEditable: value }));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setIsDelete = (value) => {
|
|
|
|
|
if (!isCanvasView) return;
|
|
|
|
|
updateDash(view, s => ({ ...s, isDelete: value }));
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-11 14:17:21 -07:00
|
|
|
const beginEditIfNeeded = () => {
|
2026-01-30 19:02:31 +00:00
|
|
|
if (!isCanvasView) return;
|
|
|
|
|
updateDash(view, s => {
|
|
|
|
|
if(s.isEditable) return s; // already editing
|
|
|
|
|
return {
|
|
|
|
|
...s,
|
|
|
|
|
originalLayout: s.layout,
|
|
|
|
|
originalWidgets: s.widgetArray,
|
|
|
|
|
};
|
|
|
|
|
});
|
2025-08-11 14:17:21 -07:00
|
|
|
};
|
|
|
|
|
|
2025-07-31 12:40:46 -07:00
|
|
|
const onCancel = () => {
|
2026-01-30 19:02:31 +00:00
|
|
|
if (!isCanvasView) return;
|
|
|
|
|
|
|
|
|
|
updateDash(view, s => ({
|
|
|
|
|
...s,
|
|
|
|
|
layout: (s.originalLayout?.length ? s.originalLayout : s.layout).map(it => ({
|
|
|
|
|
...it,
|
|
|
|
|
static: true,
|
|
|
|
|
})),
|
|
|
|
|
widgetArray: s.originalWidgets ?? s.widgetArray,
|
|
|
|
|
isEditable: false,
|
|
|
|
|
isDelete: false,
|
|
|
|
|
originalLayout: [],
|
|
|
|
|
originalWidgets: [],
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onLayoutChange = (newLayout) => {
|
|
|
|
|
setLayout(newLayout);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleViewChange = (nextView) => {
|
|
|
|
|
// prevent nav only when current view is a widget canvas AND you're editing
|
|
|
|
|
if (CANVAS_VIEWS.has(view) && dash.isEditable) {
|
2025-08-12 09:55:57 -07:00
|
|
|
dialog.setOpen(true);
|
|
|
|
|
return;
|
2025-07-31 12:23:45 -07:00
|
|
|
}
|
|
|
|
|
setView(nextView);
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-04 15:38:19 -07:00
|
|
|
// Prop Forwarding
|
2026-01-30 19:02:31 +00:00
|
|
|
const canvasProps = {
|
|
|
|
|
view,
|
|
|
|
|
onChangeView: handleViewChange,
|
|
|
|
|
|
|
|
|
|
// active canvas props
|
|
|
|
|
layout: dash.layout,
|
|
|
|
|
widgetArray: dash.widgetArray,
|
|
|
|
|
isEditable: dash.isEditable,
|
|
|
|
|
isDelete: dash.isDelete,
|
|
|
|
|
|
|
|
|
|
// handler functions
|
|
|
|
|
setLayout,
|
|
|
|
|
setWidgetArray,
|
|
|
|
|
setIsEditable,
|
|
|
|
|
setIsDelete,
|
|
|
|
|
beginEditIfNeeded,
|
|
|
|
|
onCancel,
|
|
|
|
|
onLayoutChange,
|
2025-08-04 15:38:19 -07:00
|
|
|
};
|
|
|
|
|
const sidebarProps = { view, onChangeView: handleViewChange, darkMode, setDarkMode };
|
|
|
|
|
const headerProps = { onChangeView: handleViewChange };
|
|
|
|
|
|
2025-07-23 11:01:25 -07:00
|
|
|
return (
|
2025-08-12 09:55:57 -07:00
|
|
|
<>
|
|
|
|
|
<DialogPopup dialog={dialog} />
|
2025-07-23 21:31:18 +00:00
|
|
|
<Grid css={styles}
|
|
|
|
|
templateRows="1fr 11fr"
|
2025-07-31 14:51:25 -07:00
|
|
|
// templateColumns="1fr 20fr"
|
|
|
|
|
templateColumns="auto 1fr"
|
2025-07-23 21:31:18 +00:00
|
|
|
height="100vh"
|
|
|
|
|
width="100vw"
|
|
|
|
|
>
|
|
|
|
|
<GridItem rowSpan={1} colSpan={2}>
|
2025-08-04 15:38:19 -07:00
|
|
|
<Header {...headerProps}/>
|
2025-07-23 21:31:18 +00:00
|
|
|
</GridItem>
|
|
|
|
|
<GridItem colSpan={1}>
|
2025-08-04 15:38:19 -07:00
|
|
|
<Sidebar {...sidebarProps}/>
|
2025-07-23 21:31:18 +00:00
|
|
|
</GridItem>
|
2025-07-24 13:48:41 -07:00
|
|
|
<GridItem colSpan={1} width="100%" overflow="auto">
|
2025-08-04 15:38:19 -07:00
|
|
|
<Canvas {...canvasProps}/>
|
2025-08-04 12:04:33 -07:00
|
|
|
</GridItem>
|
2025-07-23 21:31:18 +00:00
|
|
|
</Grid>
|
2025-08-12 09:55:57 -07:00
|
|
|
</>
|
2025-07-23 11:01:25 -07:00
|
|
|
);
|
2025-07-23 21:31:18 +00:00
|
|
|
}
|