From 364ce0b7527d3626ee89cd50e3ec2683505119b4 Mon Sep 17 00:00:00 2001 From: "Daniel S. Hansen" Date: Thu, 31 Jul 2025 12:23:45 -0700 Subject: [PATCH 1/2] Save point, Sidebar prevents navigation without first saving or cancelling current changes --- .../src/components/layout/Canvas/Canvas.jsx | 12 ++++++-- .../layout/Canvas/views/DashboardCanvas.jsx | 26 ++++++++--------- client/src/components/layout/Dashboard.jsx | 28 +++++++++++++++++-- .../src/components/layout/Header/Header.jsx | 3 +- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/client/src/components/layout/Canvas/Canvas.jsx b/client/src/components/layout/Canvas/Canvas.jsx index 7c02fd1..82d1258 100644 --- a/client/src/components/layout/Canvas/Canvas.jsx +++ b/client/src/components/layout/Canvas/Canvas.jsx @@ -12,7 +12,7 @@ import SettingsCanvas from "./views/SettingsCanvas" import { DashboardCanvas } from "./views/DashboardCanvas"; import { DASHBOARD_VIEWS } from "@/constants/viewKeys"; -export default function Canvas({ view, setView, layout, setLayout, onLayoutChange, darkMode }) { +export default function Canvas({ isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView, darkMode }) { const recipe = useRecipe({ key: "canvas" }); const styles = recipe(); @@ -21,11 +21,19 @@ export default function Canvas({ view, setView, layout, setLayout, onLayoutChang {/* Check if view is a tab inside the Canvas Header */} { DASHBOARD_VIEWS.includes(view) && ( )} {view === "global" && } diff --git a/client/src/components/layout/Canvas/views/DashboardCanvas.jsx b/client/src/components/layout/Canvas/views/DashboardCanvas.jsx index 476b6da..8b7e7be 100644 --- a/client/src/components/layout/Canvas/views/DashboardCanvas.jsx +++ b/client/src/components/layout/Canvas/views/DashboardCanvas.jsx @@ -11,11 +11,11 @@ import { CancelButton } from "../components/CancelButton"; import { SaveButton } from "../components/SaveButton"; 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); +export function DashboardCanvas({ isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView }) { + // 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 [originalLayout, setOriginalLayout] = useState([]); const onAddWidget = () => { const newId = newCounter.toString(); @@ -100,15 +100,15 @@ export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChan // Prevent navigation away from 'My Dashboard' while in Edit mode onValueChange={(e) => { const nextView = e.value; - - if (view === "widget" && isEditable && nextView !== "widget") { - const confirmed = window.confirm( - "You have unsaved changes. Do you wish to discard them and continue?" - ); - if (!confirmed) return; - onCancel(); - } - setView(nextView); + onChangeView(nextView); + // if (view === "widget" && isEditable && nextView !== "widget") { + // const confirmed = window.confirm( + // "You have unsaved changes. Do you wish to discard them and continue?" + // ); + // if (!confirmed) return; + // onCancel(); + // } + // setView(nextView); }} > { + if (view === "widget" && isEditable) { + const confirmed = window.confirm( + "You have unsaved changes. Discard them and continue?" + ); + if (!confirmed) return; + onCancel(); // Discards the layout + } + setView(nextView); + }; + return ( <> @@ -37,7 +52,9 @@ export default function Dashboard() { darkMode={ darkMode } setDarkMode={ setDarkMode } view={ view } - onChangeView={ setView } + onChangeView={ handleViewChange } + isEditabe={ isEditable } + // onChangeView={ setView } /> @@ -47,7 +64,14 @@ export default function Dashboard() { setView={ setView } layout={ layout } setLayout={ setLayout } + originalLayout={ originalLayout } + setOriginalLayout={ setOriginalLayout } onLayoutChange={( newLayout ) => setLayout( newLayout )} + onChangeView={ handleViewChange } + isEditable={ isEditable } + setIsEditable={ setIsEditable } + isDelete={ isDelete } + setIsDelete={ setIsDelete } /> diff --git a/client/src/components/layout/Header/Header.jsx b/client/src/components/layout/Header/Header.jsx index 79c5f41..d2c3fa4 100644 --- a/client/src/components/layout/Header/Header.jsx +++ b/client/src/components/layout/Header/Header.jsx @@ -8,7 +8,7 @@ const user = { // avatar: "src\\assets\\user_profile.svg" } -export default function Header({ view, setView }) { +export default function Header({ view, setView, onChangeView }) { const recipe = useRecipe({ key: "header" }); const styles = recipe(); @@ -25,6 +25,7 @@ export default function Header({ view, setView }) { avatar={user.avatar} view={ view } setView={ setView } + onChangeView={ onChangeView } /> From 80a0515bb179fbfd382506f16bb5e20c293c58e2 Mon Sep 17 00:00:00 2001 From: "Daniel S. Hansen" Date: Thu, 31 Jul 2025 12:40:46 -0700 Subject: [PATCH 2/2] Add navigation prevention while in Edit mode to Avatar menu options in Header --- .../src/components/layout/Canvas/Canvas.jsx | 3 ++- .../layout/Canvas/views/DashboardCanvas.jsx | 27 ++++++++++--------- client/src/components/layout/Dashboard.jsx | 14 ++++++++++ .../layout/Header/UserAvatarMenu.jsx | 6 ++--- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/client/src/components/layout/Canvas/Canvas.jsx b/client/src/components/layout/Canvas/Canvas.jsx index 82d1258..5e20a67 100644 --- a/client/src/components/layout/Canvas/Canvas.jsx +++ b/client/src/components/layout/Canvas/Canvas.jsx @@ -12,7 +12,7 @@ import SettingsCanvas from "./views/SettingsCanvas" import { DashboardCanvas } from "./views/DashboardCanvas"; import { DASHBOARD_VIEWS } from "@/constants/viewKeys"; -export default function Canvas({ isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView, darkMode }) { +export default function Canvas({ onCancel, isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView, darkMode }) { const recipe = useRecipe({ key: "canvas" }); const styles = recipe(); @@ -30,6 +30,7 @@ export default function Canvas({ isEditable, setIsEditable, isDelete, setIsDelet setOriginalLayout={ setOriginalLayout } onLayoutChange={ onLayoutChange } onChangeView={ onChangeView } + onCancel={ onCancel } isEditable={ isEditable } setIsEditable={ setIsEditable } isDelete={ isDelete } diff --git a/client/src/components/layout/Canvas/views/DashboardCanvas.jsx b/client/src/components/layout/Canvas/views/DashboardCanvas.jsx index 8b7e7be..b27c5b5 100644 --- a/client/src/components/layout/Canvas/views/DashboardCanvas.jsx +++ b/client/src/components/layout/Canvas/views/DashboardCanvas.jsx @@ -11,7 +11,7 @@ import { CancelButton } from "../components/CancelButton"; import { SaveButton } from "../components/SaveButton"; import DeleteAllButton from "../components/DeleteAllButton"; -export function DashboardCanvas({ isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView }) { +export function DashboardCanvas({ onCancel, isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView }) { // const [isEditable, setIsEditable] = useState(false); // editable=true means static=false (vice versa) // const [isDelete, setIsDelete] = useState(false); const [newCounter, setNewCounter] = useState(3); @@ -55,18 +55,19 @@ export function DashboardCanvas({ isEditable, setIsEditable, isDelete, setIsDele setOriginalLayout([]) // Clear backup layout } - const onCancel = () => { - console.log("canceling changes..."); - if (originalLayout.length) { - setLayout(originalLayout.map(item => ({ - ...item, - static: true - }))); - }; - setIsEditable(false); - setIsDelete(false); - setOriginalLayout([]); - } + // Lifting up to Dashboard.jsx for prop drilling + // const onCancel = () => { + // console.log("canceling changes..."); + // if (originalLayout.length) { + // setLayout(originalLayout.map(item => ({ + // ...item, + // static: true + // }))); + // }; + // setIsEditable(false); + // setIsDelete(false); + // setOriginalLayout([]); + // } const onDeleteWidgets = () => { console.log("Start deleting widgets..."); diff --git a/client/src/components/layout/Dashboard.jsx b/client/src/components/layout/Dashboard.jsx index 28dde76..4a61747 100644 --- a/client/src/components/layout/Dashboard.jsx +++ b/client/src/components/layout/Dashboard.jsx @@ -20,6 +20,19 @@ export default function Dashboard() { { i: '2', x: 6, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true } ]); + const onCancel = () => { + console.log("canceling changes..."); + if (originalLayout.length) { + setLayout(originalLayout.map(item => ({ + ...item, + static: true + }))); + }; + setIsEditable(false); + setIsDelete(false); + setOriginalLayout([]); + } + // Prevent navigation while in Edit mode without saving or cancelling const handleViewChange = (nextView) => { if (view === "widget" && isEditable) { @@ -68,6 +81,7 @@ export default function Dashboard() { setOriginalLayout={ setOriginalLayout } onLayoutChange={( newLayout ) => setLayout( newLayout )} onChangeView={ handleViewChange } + onCancel={ onCancel } isEditable={ isEditable } setIsEditable={ setIsEditable } isDelete={ isDelete } diff --git a/client/src/components/layout/Header/UserAvatarMenu.jsx b/client/src/components/layout/Header/UserAvatarMenu.jsx index 250542c..3158e89 100644 --- a/client/src/components/layout/Header/UserAvatarMenu.jsx +++ b/client/src/components/layout/Header/UserAvatarMenu.jsx @@ -1,7 +1,7 @@ import { Avatar, Menu, Portal, Box } from "@chakra-ui/react" -export default function UserAvatarMenu({ view, setView, name, avatar }) { +export default function UserAvatarMenu({ view, setView, onChangeView, name, avatar }) { return ( @@ -21,10 +21,10 @@ export default function UserAvatarMenu({ view, setView, name, avatar }) { {/* zIndex: overlay = 1300 */} - setView("account")}> + onChangeView("account")}> Account - setView("settings")}> + onChangeView("settings")}> Settings console.log("Log out triggered")}>