2026-01-30 19:02:31 +00:00
|
|
|
import { useState } from "react";
|
|
|
|
|
import { HStack, Flex, Tabs, Box } from "@chakra-ui/react";
|
2025-08-05 15:08:18 -07:00
|
|
|
import MyDashboard from "./HomeTabs/MyDashboard";
|
2025-08-05 12:57:18 -07:00
|
|
|
import { SearchBar } from "../CanvasComponents/SearchBar";
|
|
|
|
|
import { SettingsButton } from "../CanvasComponents/SettingsButton";
|
|
|
|
|
import { DailyActivity } from "./HomeTabs/DailyActivity";
|
|
|
|
|
import { Gas } from "./HomeTabs/Gas";
|
|
|
|
|
import { Seismic } from "./HomeTabs/Seismic";
|
2026-01-30 19:02:31 +00:00
|
|
|
import { Deformation } from "./HomeTabs/Deformation";
|
2025-08-05 12:57:18 -07:00
|
|
|
import { RemoteSensing } from "./HomeTabs/RemoteSensing";
|
|
|
|
|
import { CancelButton } from "../CanvasComponents/CancelButton";
|
|
|
|
|
import { SaveButton } from "../CanvasComponents/SaveButton";
|
|
|
|
|
import DeleteAllButton from "../CanvasComponents/DeleteAllButton";
|
2025-08-06 16:27:08 -07:00
|
|
|
import { v4 as uuid } from 'uuid';
|
2025-09-05 20:15:00 +00:00
|
|
|
import { CanvasHeaderTabs } from '../CanvasComponents/CanvasHeaderTabs';
|
|
|
|
|
import { DASHBOARD_TABS } from "@/constants/viewKeys";
|
2026-01-30 19:02:31 +00:00
|
|
|
import { WIDGET_REGISTRY } from "@/constants/widgetRegistry";
|
|
|
|
|
import { WIDGET_INSPECTOR_REGISTRY } from "@/constants/widgetInspectorRegistry";
|
|
|
|
|
import WidgetInspector from "../CanvasComponents/WidgetInspector";
|
2025-08-06 16:27:08 -07:00
|
|
|
|
2025-08-12 15:27:33 -07:00
|
|
|
export default function Home(props) {
|
2025-08-04 15:38:19 -07:00
|
|
|
// deconstructing props
|
2026-01-30 19:02:31 +00:00
|
|
|
const { view, layout, setLayout, onLayoutChange, onChangeView, onCancel, isDelete, setIsDelete,
|
|
|
|
|
beginEditIfNeeded, isEditable, setIsEditable, widgetArray, setWidgetArray } = props;
|
2025-07-24 12:08:10 -07:00
|
|
|
|
2026-01-30 19:02:31 +00:00
|
|
|
const [inspectorOpen, setInspectorOpen] = useState(false);
|
|
|
|
|
const [inspectorTarget, setInspectorTarget] = useState(null);
|
|
|
|
|
|
|
|
|
|
const openInspector = ({ id, widgetKey }) => {
|
|
|
|
|
setInspectorTarget({ id, widgetKey, view });
|
|
|
|
|
setInspectorOpen(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function appendWidget(widgetKey, overrides = {}) {
|
2025-08-11 14:17:21 -07:00
|
|
|
beginEditIfNeeded(); // Save state before anything else
|
2026-01-30 19:02:31 +00:00
|
|
|
// Place canvas in edit mode upon adding a new widget
|
|
|
|
|
setIsEditable(true);
|
|
|
|
|
setIsDelete(true);
|
|
|
|
|
|
|
|
|
|
const def = WIDGET_REGISTRY[widgetKey];
|
|
|
|
|
if(!def) {
|
|
|
|
|
console.warn("appendWidget: unknown widgetKey:", widgetKey);
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Widget Inspector Configuration Settings
|
|
|
|
|
const inspectorDef = WIDGET_INSPECTOR_REGISTRY[widgetKey];
|
|
|
|
|
const defaultConfig = inspectorDef?.defaultConfig ?? {};
|
|
|
|
|
|
2025-08-11 14:17:21 -07:00
|
|
|
const newID = uuid(); // could be replaced with something else like guid
|
2025-08-11 14:53:17 -07:00
|
|
|
const COLS = 16;
|
|
|
|
|
|
2026-01-30 19:02:31 +00:00
|
|
|
if (widgetKey === "gas_measurements") {
|
|
|
|
|
setInspectorTarget({ view, id: newID, widgetKey });
|
|
|
|
|
setInspectorOpen(true);
|
|
|
|
|
}
|
2025-08-11 14:17:21 -07:00
|
|
|
|
2026-01-30 19:02:31 +00:00
|
|
|
const {
|
|
|
|
|
w = def.defaults.w,
|
|
|
|
|
h = def.defaults.h,
|
|
|
|
|
minW = def.defaults.minW,
|
|
|
|
|
minH = def.defaults.minH,
|
|
|
|
|
maxW = def.defaults.maxW,
|
|
|
|
|
maxH = def.defaults.maxH,
|
|
|
|
|
} = overrides;
|
|
|
|
|
|
|
|
|
|
// DEBUG
|
|
|
|
|
console.log("after add:", { widgetKey, layoutLen: layout.length, widgetLen: widgetArray.length });
|
|
|
|
|
|
|
|
|
|
// IMPORTANT: use functional updates so you don't rely on stale widgetArray/layout
|
|
|
|
|
setWidgetArray(prev => [
|
|
|
|
|
...prev,
|
|
|
|
|
{
|
|
|
|
|
i: newID,
|
|
|
|
|
widgetKey,
|
|
|
|
|
// config: JSON.parse(JSON.stringify(defaultConfig)), // Can use this if Safari complains
|
|
|
|
|
config: structuredClone(defaultConfig)
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
2025-07-24 12:08:10 -07:00
|
|
|
setLayout(prev => [
|
2025-08-04 12:04:33 -07:00
|
|
|
...prev,
|
|
|
|
|
{
|
2025-08-11 14:17:21 -07:00
|
|
|
i: newID,
|
2026-01-30 19:02:31 +00:00
|
|
|
x: (prev.length * w) % COLS,
|
2025-08-11 14:53:17 -07:00
|
|
|
y: 0,
|
2026-01-30 19:02:31 +00:00
|
|
|
w,
|
|
|
|
|
h,
|
|
|
|
|
minW,
|
|
|
|
|
minH,
|
|
|
|
|
maxW,
|
|
|
|
|
maxH,
|
2025-08-06 12:03:48 -07:00
|
|
|
static: false,
|
|
|
|
|
resizeHandles: ['se']
|
2025-08-11 14:17:21 -07:00
|
|
|
},
|
2025-07-24 12:08:10 -07:00
|
|
|
]);
|
2025-08-06 16:27:08 -07:00
|
|
|
}
|
|
|
|
|
|
2026-01-30 19:02:31 +00:00
|
|
|
function updateWidgetConfig(id, patchOrFn) {
|
|
|
|
|
setWidgetArray(prev =>
|
|
|
|
|
prev.map(w => {
|
|
|
|
|
if (w.i !== id) return w;
|
|
|
|
|
|
|
|
|
|
const nextConfig =
|
|
|
|
|
typeof patchOrFn === "function"
|
|
|
|
|
? patchOrFn(w.config ?? {})
|
|
|
|
|
: {...(w.config ?? {}), ...(patchOrFn ?? {}) };
|
|
|
|
|
|
|
|
|
|
return { ...w, config: nextConfig };
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 11:40:57 -07:00
|
|
|
const onSave = () => {
|
2025-07-28 12:44:17 -07:00
|
|
|
console.log("saving widgets...");
|
2025-07-31 14:01:08 -07:00
|
|
|
// Set all widgets back to static: true
|
|
|
|
|
setLayout(prevLayout =>
|
|
|
|
|
prevLayout.map(item => ({
|
|
|
|
|
...item,
|
2025-07-25 11:40:57 -07:00
|
|
|
static: true
|
2025-07-31 14:01:08 -07:00
|
|
|
}))
|
|
|
|
|
);
|
2025-07-25 11:40:57 -07:00
|
|
|
setIsEditable(false);
|
2025-07-28 12:44:17 -07:00
|
|
|
setIsDelete(false);
|
2025-07-24 16:05:42 -07:00
|
|
|
}
|
|
|
|
|
|
2025-08-12 15:27:33 -07:00
|
|
|
const onEditWidgets = () => {
|
2025-07-28 12:44:17 -07:00
|
|
|
console.log("Start deleting widgets...");
|
2025-08-12 13:57:58 -07:00
|
|
|
beginEditIfNeeded(); // Save state before anything else
|
2025-08-12 15:27:33 -07:00
|
|
|
setLayout(prevLayout =>
|
|
|
|
|
prevLayout.map(item => ({
|
|
|
|
|
...item,
|
|
|
|
|
static: false
|
|
|
|
|
}))
|
|
|
|
|
);
|
2025-07-28 12:44:17 -07:00
|
|
|
setIsEditable(true);
|
|
|
|
|
setIsDelete(true);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 15:38:19 -07:00
|
|
|
const DeleteWidget = (ID) => {
|
|
|
|
|
console.log("Deleting widget with id: ", ID);
|
|
|
|
|
setLayout(layout => layout.filter(w => w.i !== ID));
|
2025-08-05 15:19:46 -07:00
|
|
|
setWidgetArray(widgetArray => widgetArray.filter(w => w.i !== ID));
|
2025-07-24 12:08:10 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-31 09:50:40 -07:00
|
|
|
const DeleteAll = () => {
|
|
|
|
|
const confirm = window.confirm(
|
|
|
|
|
"Are you sure you want to delete all widgets?"
|
|
|
|
|
);
|
|
|
|
|
if (confirm) {
|
|
|
|
|
setLayout([]);
|
2025-08-04 12:04:33 -07:00
|
|
|
setWidgetArray([]);
|
2025-07-31 09:50:40 -07:00
|
|
|
onSave();
|
|
|
|
|
}
|
|
|
|
|
else return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 15:38:19 -07:00
|
|
|
// Prop Forwarding
|
2026-01-30 19:02:31 +00:00
|
|
|
const MyDashboardProps = { view, layout, onLayoutChange, isEditable,
|
|
|
|
|
beginEditIfNeeded, isDelete, DeleteWidget, widgetArray, appendWidget,
|
|
|
|
|
openInspector
|
|
|
|
|
};
|
|
|
|
|
const settingsButtonProps = { view, onEditWidgets, appendWidget };
|
2025-08-04 15:38:19 -07:00
|
|
|
|
2025-07-24 08:52:20 -07:00
|
|
|
return (
|
2025-07-24 12:08:10 -07:00
|
|
|
/* HEADER */
|
2025-07-29 09:59:17 -07:00
|
|
|
<Tabs.Root
|
2025-08-05 17:12:59 -07:00
|
|
|
width="100%"
|
|
|
|
|
height="100%"
|
2025-07-29 09:59:17 -07:00
|
|
|
lazyMount
|
|
|
|
|
value={ view }
|
2025-08-04 15:38:19 -07:00
|
|
|
variant="line"
|
2025-07-29 12:08:36 -07:00
|
|
|
// Prevent navigation away from 'My Dashboard' while in Edit mode
|
2025-07-29 11:51:33 -07:00
|
|
|
onValueChange={(e) => {
|
|
|
|
|
const nextView = e.value;
|
2025-07-31 12:23:45 -07:00
|
|
|
onChangeView(nextView);
|
2025-07-29 11:51:33 -07:00
|
|
|
}}
|
2025-07-29 09:59:17 -07:00
|
|
|
>
|
2025-07-24 08:52:20 -07:00
|
|
|
<Flex
|
|
|
|
|
as="header"
|
|
|
|
|
position="sticky"
|
|
|
|
|
top="0"
|
2025-07-28 11:44:25 -07:00
|
|
|
zIndex="dropdown" // zIndex: 1100
|
2025-07-24 08:52:20 -07:00
|
|
|
bg="base200"
|
2025-09-05 20:15:00 +00:00
|
|
|
pl={5}
|
|
|
|
|
pr={2}
|
2025-07-29 11:08:36 -07:00
|
|
|
pt={3}
|
2025-08-06 16:25:54 -07:00
|
|
|
pb={2}
|
2025-07-24 08:52:20 -07:00
|
|
|
align="center"
|
|
|
|
|
justify="space-between"
|
2025-08-05 17:12:59 -07:00
|
|
|
width="100%"
|
2025-07-24 08:52:20 -07:00
|
|
|
>
|
2025-09-05 20:15:00 +00:00
|
|
|
<CanvasHeaderTabs tabs={DASHBOARD_TABS} />
|
2025-08-05 17:12:59 -07:00
|
|
|
<HStack width="258px" justifyContent="flex-end">
|
2025-07-31 09:50:40 -07:00
|
|
|
{isEditable ? (
|
2025-07-25 11:40:57 -07:00
|
|
|
<>
|
2025-07-31 09:50:40 -07:00
|
|
|
<SaveButton onSave={ onSave } />
|
|
|
|
|
<CancelButton onCancel={ onCancel } />
|
|
|
|
|
{isDelete ? (
|
|
|
|
|
<DeleteAllButton onDeleteAll={ DeleteAll } />
|
|
|
|
|
):(null)
|
|
|
|
|
}
|
2025-07-25 11:40:57 -07:00
|
|
|
</>
|
|
|
|
|
):(
|
|
|
|
|
<>
|
|
|
|
|
<SearchBar />
|
2025-08-04 15:38:19 -07:00
|
|
|
<SettingsButton {...settingsButtonProps}/>
|
2025-07-25 11:40:57 -07:00
|
|
|
</>
|
|
|
|
|
)}
|
2025-07-24 08:52:20 -07:00
|
|
|
</HStack>
|
|
|
|
|
</Flex>
|
|
|
|
|
|
2025-07-24 12:08:10 -07:00
|
|
|
{/* CANVAS */}
|
2025-08-05 15:08:18 -07:00
|
|
|
<Tabs.Content value="mydashboard">
|
2026-01-30 19:02:31 +00:00
|
|
|
<Flex height="100%" width="100%" gap="3">
|
|
|
|
|
<Box flex="1" minW="0">
|
|
|
|
|
<MyDashboard {...MyDashboardProps}/>
|
|
|
|
|
</Box>
|
|
|
|
|
|
|
|
|
|
{inspectorOpen && inspectorTarget && (
|
|
|
|
|
<WidgetInspector
|
|
|
|
|
open={inspectorOpen}
|
|
|
|
|
onClose={() => setInspectorOpen(false)}
|
|
|
|
|
target={inspectorTarget}
|
|
|
|
|
widgetArray={widgetArray}
|
|
|
|
|
updateWidgetConfig={updateWidgetConfig}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</Flex>
|
2025-07-24 08:52:20 -07:00
|
|
|
</Tabs.Content>
|
2026-01-30 19:02:31 +00:00
|
|
|
|
2025-07-24 08:52:20 -07:00
|
|
|
<Tabs.Content value="gas">
|
2026-01-30 19:02:31 +00:00
|
|
|
<MyDashboard {...MyDashboardProps}/>
|
2025-07-24 08:52:20 -07:00
|
|
|
</Tabs.Content>
|
|
|
|
|
<Tabs.Content value="seismic">
|
|
|
|
|
<Seismic />
|
|
|
|
|
</Tabs.Content>
|
2025-07-29 11:04:29 -07:00
|
|
|
<Tabs.Content value="remote">
|
2025-07-24 08:52:20 -07:00
|
|
|
<RemoteSensing />
|
|
|
|
|
</Tabs.Content>
|
2025-07-29 11:04:29 -07:00
|
|
|
<Tabs.Content value="daily">
|
2025-07-24 08:52:20 -07:00
|
|
|
<DailyActivity />
|
|
|
|
|
</Tabs.Content>
|
2026-01-30 19:02:31 +00:00
|
|
|
<Tabs.Content value="deformation">
|
|
|
|
|
<Deformation />
|
|
|
|
|
</Tabs.Content>
|
2025-07-24 08:52:20 -07:00
|
|
|
</Tabs.Root>
|
|
|
|
|
);
|
|
|
|
|
}
|